Tankey Game  1.0
Tankey es un juego sobre un gorila. Este gorila trepa por un árbol infinito, pero a medida que sube se enfrenta a un problema: debe esquivar las ramas que se avecinan sobre él. Moviéndose de un lado al otro... ¿Podrá lograrlo?
miniaudio.h
Ir a la documentación de este archivo.
1 /*
2 Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file.
3 miniaudio (formerly mini_al) - v0.9.8 - 2019-10-07
4 
5 David Reid - davidreidsoftware@gmail.com
6 
7 https://github.com/dr-soft/miniaudio
8 */
9 
10 /*
11 MAJOR CHANGES IN VERSION 0.9
12 ============================
13 Version 0.9 includes major API changes, centered mostly around full-duplex and the rebrand to "miniaudio". Before I go into
14 detail about the major changes I would like to apologize. I know it's annoying dealing with breaking API changes, but I think
15 it's best to get these changes out of the way now while the library is still relatively young and unknown.
16 
17 There's been a lot of refactoring with this release so there's a good chance a few bugs have been introduced. I apologize in
18 advance for this. You may want to hold off on upgrading for the short term if you're worried. If mini_al v0.8.14 works for
19 you, and you don't need full-duplex support, you can avoid upgrading (though you won't be getting future bug fixes).
20 
21 
22 Rebranding to "miniaudio"
23 -------------------------
24 The decision was made to rename mini_al to miniaudio. Don't worry, it's the same project. The reason for this is simple:
25 
26 1) Having the word "audio" in the title makes it immediately clear that the library is related to audio; and
27 2) I don't like the look of the underscore.
28 
29 This rebrand has necessitated a change in namespace from "mal" to "ma". I know this is annoying, and I apologize, but it's
30 better to get this out of the road now rather than later. Also, since there are necessary API changes for full-duplex support
31 I think it's better to just get the namespace change over and done with at the same time as the full-duplex changes. I'm hoping
32 this will be the last of the major API changes. Fingers crossed!
33 
34 The implementation define is now "#define MINIAUDIO_IMPLEMENTATION". You can also use "#define MA_IMPLEMENTATION" if that's
35 your preference.
36 
37 
38 Full-Duplex Support
39 -------------------
40 The major feature added to version 0.9 is full-duplex. This has necessitated a few API changes.
41 
42 1) The data callback has now changed. Previously there was one type of callback for playback and another for capture. I wanted
43  to avoid a third callback just for full-duplex so the decision was made to break this API and unify the callbacks. Now,
44  there is just one callback which is the same for all three modes (playback, capture, duplex). The new callback looks like
45  the following:
46 
47  void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
48 
49  This callback allows you to move data straight out of the input buffer and into the output buffer in full-duplex mode. In
50  playback-only mode, pInput will be null. Likewise, pOutput will be null in capture-only mode. The sample count is no longer
51  returned from the callback since it's not necessary for miniaudio anymore.
52 
53 2) The device config needed to change in order to support full-duplex. Full-duplex requires the ability to allow the client
54  to choose a different PCM format for the playback and capture sides. The old ma_device_config object simply did not allow
55  this and needed to change. With these changes you now specify the device ID, format, channels, channel map and share mode
56  on a per-playback and per-capture basis (see example below). The sample rate must be the same for playback and capture.
57 
58  Since the device config API has changed I have also decided to take the opportunity to simplify device initialization. Now,
59  the device ID, device type and callback user data are set in the config. ma_device_init() is now simplified down to taking
60  just the context, device config and a pointer to the device object being initialized. The rationale for this change is that
61  it just makes more sense to me that these are set as part of the config like everything else.
62 
63  Example device initialization:
64 
65  ma_device_config config = ma_device_config_init(ma_device_type_duplex); // Or ma_device_type_playback or ma_device_type_capture.
66  config.playback.pDeviceID = &myPlaybackDeviceID; // Or NULL for the default playback device.
67  config.playback.format = ma_format_f32;
68  config.playback.channels = 2;
69  config.capture.pDeviceID = &myCaptureDeviceID; // Or NULL for the default capture device.
70  config.capture.format = ma_format_s16;
71  config.capture.channels = 1;
72  config.sampleRate = 44100;
73  config.dataCallback = data_callback;
74  config.pUserData = &myUserData;
75 
76  result = ma_device_init(&myContext, &config, &device);
77  if (result != MA_SUCCESS) {
78  ... handle error ...
79  }
80 
81  Note that the "onDataCallback" member of ma_device_config has been renamed to "dataCallback". Also, "onStopCallback" has
82  been renamed to "stopCallback".
83 
84 This is the first pass for full-duplex and there is a known bug. You will hear crackling on the following backends when sample
85 rate conversion is required for the playback device:
86  - Core Audio
87  - JACK
88  - AAudio
89  - OpenSL
90  - WebAudio
91 
92 In addition to the above, not all platforms have been absolutely thoroughly tested simply because I lack the hardware for such
93 thorough testing. If you experience a bug, an issue report on GitHub or an email would be greatly appreciated (and a sample
94 program that reproduces the issue if possible).
95 
96 
97 Other API Changes
98 -----------------
99 In addition to the above, the following API changes have been made:
100 
101 - The log callback is no longer passed to ma_context_config_init(). Instead you need to set it manually after initialization.
102 - The onLogCallback member of ma_context_config has been renamed to "logCallback".
103 - The log callback now takes a logLevel parameter. The new callback looks like: void log_callback(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
104  - You can use ma_log_level_to_string() to convert the logLevel to human readable text if you want to log it.
105 - Some APIs have been renamed:
106  - mal_decoder_read() -> ma_decoder_read_pcm_frames()
107  - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame()
108  - mal_sine_wave_read() -> ma_sine_wave_read_f32()
109  - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex()
110 - Some APIs have been removed:
111  - mal_device_get_buffer_size_in_bytes()
112  - mal_device_set_recv_callback()
113  - mal_device_set_send_callback()
114  - mal_src_set_input_sample_rate()
115  - mal_src_set_output_sample_rate()
116 - Error codes have been rearranged. If you're a binding maintainer you will need to update.
117 - The ma_backend enums have been rearranged to priority order. The rationale for this is to simplify automatic backend selection
118  and to make it easier to see the priority. If you're a binding maintainer you will need to update.
119 - ma_dsp has been renamed to ma_pcm_converter. The rationale for this change is that I'm expecting "ma_dsp" to conflict with
120  some future planned high-level APIs.
121 - For functions that take a pointer/count combo, such as ma_decoder_read_pcm_frames(), the parameter order has changed so that
122  the pointer comes before the count. The rationale for this is to keep it consistent with things like memcpy().
123 
124 
125 Miscellaneous Changes
126 ---------------------
127 The following miscellaneous changes have also been made.
128 
129 - The AAudio backend has been added for Android 8 and above. This is Android's new "High-Performance Audio" API. (For the
130  record, this is one of the nicest audio APIs out there, just behind the BSD audio APIs).
131 - The WebAudio backend has been added. This is based on ScriptProcessorNode. This removes the need for SDL.
132 - The SDL and OpenAL backends have been removed. These were originally implemented to add support for platforms for which miniaudio
133  was not explicitly supported. These are no longer needed and have therefore been removed.
134 - Device initialization now fails if the requested share mode is not supported. If you ask for exclusive mode, you either get an
135  exclusive mode device, or an error. The rationale for this change is to give the client more control over how to handle cases
136  when the desired shared mode is unavailable.
137 - A lock-free ring buffer API has been added. There are two varients of this. "ma_rb" operates on bytes, whereas "ma_pcm_rb"
138  operates on PCM frames.
139 - The library is now licensed as a choice of Public Domain (Unlicense) _or_ MIT-0 (No Attribution) which is the same as MIT, but
140  removes the attribution requirement. The rationale for this is to support countries that don't recognize public domain.
141 */
142 
143 /*
144 ABOUT
145 =====
146 miniaudio is a single file library for audio playback and capture. It's written in C (compilable as
147 C++) and released into the public domain.
148 
149 Supported Backends:
150  - WASAPI
151  - DirectSound
152  - WinMM
153  - Core Audio (Apple)
154  - ALSA
155  - PulseAudio
156  - JACK
157  - sndio (OpenBSD)
158  - audio(4) (NetBSD and OpenBSD)
159  - OSS (FreeBSD)
160  - AAudio (Android 8.0+)
161  - OpenSL|ES (Android only)
162  - Web Audio (Emscripten)
163  - Null (Silence)
164 
165 Supported Formats:
166  - Unsigned 8-bit PCM
167  - Signed 16-bit PCM
168  - Signed 24-bit PCM (tightly packed)
169  - Signed 32-bit PCM
170  - IEEE 32-bit floating point PCM
171 
172 
173 USAGE
174 =====
175 miniaudio is a single-file library. To use it, do something like the following in one .c file.
176  #define MINIAUDIO_IMPLEMENTATION
177  #include "miniaudio.h"
178 
179 You can then #include this file in other parts of the program as you would with any other header file.
180 
181 miniaudio uses an asynchronous, callback based API. You initialize a device with a configuration (sample rate,
182 channel count, etc.) which includes the callback you want to use to handle data transmission to/from the
183 device. In the callback you either read from a data pointer in the case of playback or write to it in the case
184 of capture.
185 
186 Playback Example
187 ----------------
188  void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
189  {
190  ma_decoder* pDecoder = (ma_decoder*)pDevice->pUserData;
191  if (pDecoder == NULL) {
192  return;
193  }
194 
195  ma_decoder_read_pcm_frames(pDecoder, frameCount, pOutput);
196  }
197 
198  ...
199 
200  ma_device_config config = ma_device_config_init(ma_device_type_playback);
201  config.playback.format = decoder.outputFormat;
202  config.playback.channels = decoder.outputChannels;
203  config.sampleRate = decoder.outputSampleRate;
204  config.dataCallback = data_callback;
205  config.pUserData = &decoder;
206 
207  ma_device device;
208  if (ma_device_init(NULL, &config, &device) != MA_SUCCESS) {
209  ... An error occurred ...
210  }
211 
212  ma_device_start(&device); // The device is sleeping by default so you'll need to start it manually.
213 
214  ...
215 
216  ma_device_uninit(&device); // This will stop the device so no need to do that manually.
217 
218 
219 BUILDING
220 ========
221 miniaudio should Just Work by adding it to your project's source tree. You do not need to download or install
222 any dependencies. See below for platform-specific details.
223 
224 If you want to disable a specific backend, #define the appropriate MA_NO_* option before the implementation.
225 
226 Note that GCC and Clang requires "-msse2", "-mavx2", etc. for SIMD optimizations.
227 
228 
229 Building for Windows
230 --------------------
231 The Windows build should compile clean on all popular compilers without the need to configure any include paths
232 nor link to any libraries.
233 
234 Building for macOS and iOS
235 --------------------------
236 The macOS build should compile clean without the need to download any dependencies or link to any libraries or
237 frameworks. The iOS build needs to be compiled as Objective-C (sorry) and will need to link the relevant frameworks
238 but should Just Work with Xcode.
239 
240 Building for Linux
241 ------------------
242 The Linux build only requires linking to -ldl, -lpthread and -lm. You do not need any development packages.
243 
244 Building for BSD
245 ----------------
246 The BSD build only requires linking to -ldl, -lpthread and -lm. NetBSD uses audio(4), OpenBSD uses sndio and
247 FreeBSD uses OSS.
248 
249 Building for Android
250 --------------------
251 AAudio is the highest priority backend on Android. This should work out out of the box without needing any kind of
252 compiler configuration. Support for AAudio starts with Android 8 which means older versions will fall back to
253 OpenSL|ES which requires API level 16+.
254 
255 Building for Emscripten
256 -----------------------
257 The Emscripten build emits Web Audio JavaScript directly and should Just Work without any configuration.
258 
259 
260 NOTES
261 =====
262 - This library uses an asynchronous API for delivering and requesting audio data. Each device will have
263  it's own worker thread which is managed by the library.
264 - If ma_device_init() is called with a device that's not aligned to the 4 bytes on 32-bit or 8 bytes on
265  64-bit it will _not_ be thread-safe. The reason for this is that it depends on members of ma_device being
266  correctly aligned for atomic assignments.
267 - Sample data is always native-endian and interleaved. For example, ma_format_s16 means signed 16-bit
268  integer samples, interleaved. Let me know if you need non-interleaved and I'll look into it.
269 - The sndio backend is currently only enabled on OpenBSD builds.
270 - The audio(4) backend is supported on OpenBSD, but you may need to disable sndiod before you can use it.
271 - Automatic stream routing is enabled on a per-backend basis. Support is explicitly enabled for WASAPI
272  and Core Audio, however other backends such as PulseAudio may naturally support it, though not all have
273  been tested.
274 - The contents of the output buffer passed into the data callback will always be pre-initialized to zero
275  unless the noPreZeroedOutputBuffer config variable in ma_device_config is set to true, in which case
276  it'll be undefined which will require you to write something to the entire buffer.
277 - By default miniaudio will automatically clip samples. This only applies when the playback sample format
278  is configured as ma_format_f32. If you are doing clipping yourself, you can disable this overhead by
279  setting noClip to true in the device config.
280 
281 
282 BACKEND NUANCES
283 ===============
284 
285 WASAPI
286 ------
287 - Low-latency shared mode will be disabled when using an application-defined sample rate which is different to the
288  device's native sample rate. To work around this, set wasapi.noAutoConvertSRC to true in the device config. This
289  is due to IAudioClient3_InitializeSharedAudioStream() failing when the AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM flag is
290  specified. Setting wasapi.noAutoConvertSRC will result in miniaudio's lower quality internal resampler being used
291  instead which will in turn enable the use of low-latency shared mode.
292 
293 PulseAudio
294 ----------
295 - If you experience bad glitching/noise on Arch Linux, consider this fix from the Arch wiki:
296  https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Glitches,_skips_or_crackling
297  Alternatively, consider using a different backend such as ALSA.
298 
299 Android
300 -------
301 - To capture audio on Android, remember to add the RECORD_AUDIO permission to your manifest:
302  <uses-permission android:name="android.permission.RECORD_AUDIO" />
303 - With OpenSL|ES, only a single ma_context can be active at any given time. This is due to a limitation with OpenSL|ES.
304 - With AAudio, only default devices are enumerated. This is due to AAudio not having an enumeration API (devices are
305  enumerated through Java). You can however perform your own device enumeration through Java and then set the ID in the
306  ma_device_id structure (ma_device_id.aaudio) and pass it to ma_device_init().
307 - The backend API will perform resampling where possible. The reason for this as opposed to using miniaudio's built-in
308  resampler is to take advantage of any potential device-specific optimizations the driver may implement.
309 
310 UWP
311 ---
312 - UWP only supports default playback and capture devices.
313 - UWP requires the Microphone capability to be enabled in the application's manifest (Package.appxmanifest):
314  <Package ...>
315  ...
316  <Capabilities>
317  <DeviceCapability Name="microphone" />
318  </Capabilities>
319  </Package>
320 
321 Web Audio / Emscripten
322 ----------------------
323 - The first time a context is initialized it will create a global object called "miniaudio" whose primary purpose is to act
324  as a factory for device objects.
325 - Currently the Web Audio backend uses ScriptProcessorNode's, but this may need to change later as they've been deprecated.
326 - Google is implementing a policy in their browsers that prevent automatic media output without first receiving some kind
327  of user input. See here for details: https://developers.google.com/web/updates/2017/09/autoplay-policy-changes. Starting
328  the device may fail if you try to start playback without first handling some kind of user input.
329 
330 
331 OPTIONS
332 =======
333 #define these options before including this file.
334 
335 #define MA_NO_WASAPI
336  Disables the WASAPI backend.
337 
338 #define MA_NO_DSOUND
339  Disables the DirectSound backend.
340 
341 #define MA_NO_WINMM
342  Disables the WinMM backend.
343 
344 #define MA_NO_ALSA
345  Disables the ALSA backend.
346 
347 #define MA_NO_PULSEAUDIO
348  Disables the PulseAudio backend.
349 
350 #define MA_NO_JACK
351  Disables the JACK backend.
352 
353 #define MA_NO_COREAUDIO
354  Disables the Core Audio backend.
355 
356 #define MA_NO_SNDIO
357  Disables the sndio backend.
358 
359 #define MA_NO_AUDIO4
360  Disables the audio(4) backend.
361 
362 #define MA_NO_OSS
363  Disables the OSS backend.
364 
365 #define MA_NO_AAUDIO
366  Disables the AAudio backend.
367 
368 #define MA_NO_OPENSL
369  Disables the OpenSL|ES backend.
370 
371 #define MA_NO_WEBAUDIO
372  Disables the Web Audio backend.
373 
374 #define MA_NO_NULL
375  Disables the null backend.
376 
377 #define MA_DEFAULT_PERIODS
378  When a period count of 0 is specified when a device is initialized, it will default to this.
379 
380 #define MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY
381 #define MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE
382  When a buffer size of 0 is specified when a device is initialized it will default to a buffer of this size, depending
383  on the chosen performance profile. These can be increased or decreased depending on your specific requirements.
384 
385 #define MA_NO_DECODING
386  Disables the decoding APIs.
387 
388 #define MA_NO_DEVICE_IO
389  Disables playback and recording. This will disable ma_context and ma_device APIs. This is useful if you only want to
390  use miniaudio's data conversion and/or decoding APIs.
391 
392 #define MA_NO_STDIO
393  Disables file IO APIs.
394 
395 #define MA_NO_SSE2
396  Disables SSE2 optimizations.
397 
398 #define MA_NO_AVX2
399  Disables AVX2 optimizations.
400 
401 #define MA_NO_AVX512
402  Disables AVX-512 optimizations.
403 
404 #define MA_NO_NEON
405  Disables NEON optimizations.
406 
407 #define MA_LOG_LEVEL <Level>
408  Sets the logging level. Set level to one of the following:
409  MA_LOG_LEVEL_VERBOSE
410  MA_LOG_LEVEL_INFO
411  MA_LOG_LEVEL_WARNING
412  MA_LOG_LEVEL_ERROR
413 
414 #define MA_DEBUG_OUTPUT
415  Enable printf() debug output.
416 
417 #define MA_COINIT_VALUE
418  Windows only. The value to pass to internal calls to CoInitializeEx(). Defaults to COINIT_MULTITHREADED.
419 
420 
421 DEFINITIONS
422 ===========
423 This section defines common terms used throughout miniaudio. Unfortunately there is often ambiguity in the use of terms
424 throughout the audio space, so this section is intended to clarify how miniaudio uses each term.
425 
426 Sample
427 ------
428 A sample is a single unit of audio data. If the sample format is f32, then one sample is one 32-bit floating point number.
429 
430 Frame / PCM Frame
431 -----------------
432 A frame is a groups of samples equal to the number of channels. For a stereo stream a frame is 2 samples, a mono frame
433 is 1 sample, a 5.1 surround sound frame is 6 samples, etc. The terms "frame" and "PCM frame" are the same thing in
434 miniaudio. Note that this is different to a compressed frame. If ever miniaudio needs to refer to a compressed frame, such
435 as a FLAC frame, it will always clarify what it's referring to with something like "FLAC frame" or whatnot.
436 
437 Channel
438 -------
439 A stream of monaural audio that is emitted from an individual speaker in a speaker system, or received from an individual
440 microphone in a microphone system. A stereo stream has two channels (a left channel, and a right channel), a 5.1 surround
441 sound system has 6 channels, etc. Some audio systems refer to a channel as a complex audio stream that's mixed with other
442 channels to produce the final mix - this is completely different to miniaudio's use of the term "channel" and should not be
443 confused.
444 
445 Sample Rate
446 -----------
447 The sample rate in miniaudio is always expressed in Hz, such as 44100, 48000, etc. It's the number of PCM frames that are
448 processed per second.
449 
450 Formats
451 -------
452 Throughout miniaudio you will see references to different sample formats:
453 
454  Symbol | Description | Range
455  -------|----------------------------------------|---------------------------
456  u8 | Unsigned 8-bit integer | [0, 255]
457  s16 | Signed 16-bit integer | [-32768, 32767]
458  s24 | Signed 24-bit integer (tightly packed) | [-8388608, 8388607]
459  s32 | Signed 32-bit integer | [-2147483648, 2147483647]
460  f32 | 32-bit floating point | [-1, 1]
461 
462 All formats are native-endian.
463 */
464 
465 #ifndef miniaudio_h
466 #define miniaudio_h
467 
468 #ifdef __cplusplus
469 extern "C" {
470 #endif
471 
472 #if defined(_MSC_VER) && !defined(__clang__)
473  #pragma warning(push)
474  #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */
475  #pragma warning(disable:4324) /* structure was padded due to alignment specifier */
476 #else
477  #pragma GCC diagnostic push
478  #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
479  #if defined(__clang__)
480  #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */
481  #endif
482 #endif
483 
484 /* Platform/backend detection. */
485 #ifdef _WIN32
486  #define MA_WIN32
487  #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PC_APP || WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)
488  #define MA_WIN32_UWP
489  #else
490  #define MA_WIN32_DESKTOP
491  #endif
492 #else
493  #define MA_POSIX
494  #include <pthread.h> /* Unfortunate #include, but needed for pthread_t, pthread_mutex_t and pthread_cond_t types. */
495 
496  #ifdef __unix__
497  #define MA_UNIX
498  #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
499  #define MA_BSD
500  #endif
501  #endif
502  #ifdef __linux__
503  #define MA_LINUX
504  #endif
505  #ifdef __APPLE__
506  #define MA_APPLE
507  #endif
508  #ifdef __ANDROID__
509  #define MA_ANDROID
510  #endif
511  #ifdef __EMSCRIPTEN__
512  #define MA_EMSCRIPTEN
513  #endif
514 #endif
515 
516 #include <stddef.h> /* For size_t. */
517 
518 /* Sized types. Prefer built-in types. Fall back to stdint. */
519 #ifdef _MSC_VER
520  #if defined(__clang__)
521  #pragma GCC diagnostic push
522  #pragma GCC diagnostic ignored "-Wlanguage-extension-token"
523  #pragma GCC diagnostic ignored "-Wlong-long"
524  #pragma GCC diagnostic ignored "-Wc++11-long-long"
525  #endif
526  typedef signed __int8 ma_int8;
527  typedef unsigned __int8 ma_uint8;
528  typedef signed __int16 ma_int16;
529  typedef unsigned __int16 ma_uint16;
530  typedef signed __int32 ma_int32;
531  typedef unsigned __int32 ma_uint32;
532  typedef signed __int64 ma_int64;
533  typedef unsigned __int64 ma_uint64;
534  #if defined(__clang__)
535  #pragma GCC diagnostic pop
536  #endif
537 #else
538  #define MA_HAS_STDINT
539  #include <stdint.h>
540  typedef int8_t ma_int8;
541  typedef uint8_t ma_uint8;
542  typedef int16_t ma_int16;
544  typedef int32_t ma_int32;
546  typedef int64_t ma_int64;
548 #endif
549 
550 #ifdef MA_HAS_STDINT
552 #else
553  #if defined(_WIN32)
554  #if defined(_WIN64)
555  typedef ma_uint64 ma_uintptr;
556  #else
557  typedef ma_uint32 ma_uintptr;
558  #endif
559  #elif defined(__GNUC__)
560  #if defined(__LP64__)
561  typedef ma_uint64 ma_uintptr;
562  #else
563  typedef ma_uint32 ma_uintptr;
564  #endif
565  #else
566  typedef ma_uint64 ma_uintptr; /* Fallback. */
567  #endif
568 #endif
569 
572 #define MA_TRUE 1
573 #define MA_FALSE 0
574 
575 typedef void* ma_handle;
576 typedef void* ma_ptr;
577 typedef void (* ma_proc)(void);
578 
579 #if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED)
580 typedef ma_uint16 wchar_t;
581 #endif
582 
583 /* Define NULL for some compilers. */
584 #ifndef NULL
585 #define NULL 0
586 #endif
587 
588 #if defined(SIZE_MAX)
589  #define MA_SIZE_MAX SIZE_MAX
590 #else
591  #define MA_SIZE_MAX 0xFFFFFFFF /* When SIZE_MAX is not defined by the standard library just default to the maximum 32-bit unsigned integer. */
592 #endif
593 
594 
595 #ifdef _MSC_VER
596  #define MA_INLINE __forceinline
597 #elif defined(__GNUC__)
598  /*
599  I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
600  the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
601  case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
602  command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
603  I am using "__inline__" only when we're compiling in strict ANSI mode.
604  */
605  #if defined(__STRICT_ANSI__)
606  #define MA_INLINE __inline__ __attribute__((always_inline))
607  #else
608  #define MA_INLINE inline __attribute__((always_inline))
609  #endif
610 #else
611  #define MA_INLINE
612 #endif
613 
614 #if defined(_MSC_VER)
615  #if _MSC_VER >= 1400
616  #define MA_ALIGN(alignment) __declspec(align(alignment))
617  #endif
618 #elif !defined(__DMC__)
619  #define MA_ALIGN(alignment) __attribute__((aligned(alignment)))
620 #endif
621 #ifndef MA_ALIGN
622  #define MA_ALIGN(alignment)
623 #endif
624 
625 #ifdef _MSC_VER
626 #define MA_ALIGNED_STRUCT(alignment) MA_ALIGN(alignment) struct
627 #else
628 #define MA_ALIGNED_STRUCT(alignment) struct MA_ALIGN(alignment)
629 #endif
630 
631 /* SIMD alignment in bytes. Currently set to 64 bytes in preparation for future AVX-512 optimizations. */
632 #define MA_SIMD_ALIGNMENT 64
633 
634 
635 /* Logging levels */
636 #define MA_LOG_LEVEL_VERBOSE 4
637 #define MA_LOG_LEVEL_INFO 3
638 #define MA_LOG_LEVEL_WARNING 2
639 #define MA_LOG_LEVEL_ERROR 1
640 
641 #ifndef MA_LOG_LEVEL
642 #define MA_LOG_LEVEL MA_LOG_LEVEL_ERROR
643 #endif
644 
645 typedef struct ma_context ma_context;
646 typedef struct ma_device ma_device;
647 
649 #define MA_CHANNEL_NONE 0
650 #define MA_CHANNEL_MONO 1
651 #define MA_CHANNEL_FRONT_LEFT 2
652 #define MA_CHANNEL_FRONT_RIGHT 3
653 #define MA_CHANNEL_FRONT_CENTER 4
654 #define MA_CHANNEL_LFE 5
655 #define MA_CHANNEL_BACK_LEFT 6
656 #define MA_CHANNEL_BACK_RIGHT 7
657 #define MA_CHANNEL_FRONT_LEFT_CENTER 8
658 #define MA_CHANNEL_FRONT_RIGHT_CENTER 9
659 #define MA_CHANNEL_BACK_CENTER 10
660 #define MA_CHANNEL_SIDE_LEFT 11
661 #define MA_CHANNEL_SIDE_RIGHT 12
662 #define MA_CHANNEL_TOP_CENTER 13
663 #define MA_CHANNEL_TOP_FRONT_LEFT 14
664 #define MA_CHANNEL_TOP_FRONT_CENTER 15
665 #define MA_CHANNEL_TOP_FRONT_RIGHT 16
666 #define MA_CHANNEL_TOP_BACK_LEFT 17
667 #define MA_CHANNEL_TOP_BACK_CENTER 18
668 #define MA_CHANNEL_TOP_BACK_RIGHT 19
669 #define MA_CHANNEL_AUX_0 20
670 #define MA_CHANNEL_AUX_1 21
671 #define MA_CHANNEL_AUX_2 22
672 #define MA_CHANNEL_AUX_3 23
673 #define MA_CHANNEL_AUX_4 24
674 #define MA_CHANNEL_AUX_5 25
675 #define MA_CHANNEL_AUX_6 26
676 #define MA_CHANNEL_AUX_7 27
677 #define MA_CHANNEL_AUX_8 28
678 #define MA_CHANNEL_AUX_9 29
679 #define MA_CHANNEL_AUX_10 30
680 #define MA_CHANNEL_AUX_11 31
681 #define MA_CHANNEL_AUX_12 32
682 #define MA_CHANNEL_AUX_13 33
683 #define MA_CHANNEL_AUX_14 34
684 #define MA_CHANNEL_AUX_15 35
685 #define MA_CHANNEL_AUX_16 36
686 #define MA_CHANNEL_AUX_17 37
687 #define MA_CHANNEL_AUX_18 38
688 #define MA_CHANNEL_AUX_19 39
689 #define MA_CHANNEL_AUX_20 40
690 #define MA_CHANNEL_AUX_21 41
691 #define MA_CHANNEL_AUX_22 42
692 #define MA_CHANNEL_AUX_23 43
693 #define MA_CHANNEL_AUX_24 44
694 #define MA_CHANNEL_AUX_25 45
695 #define MA_CHANNEL_AUX_26 46
696 #define MA_CHANNEL_AUX_27 47
697 #define MA_CHANNEL_AUX_28 48
698 #define MA_CHANNEL_AUX_29 49
699 #define MA_CHANNEL_AUX_30 50
700 #define MA_CHANNEL_AUX_31 51
701 #define MA_CHANNEL_LEFT MA_CHANNEL_FRONT_LEFT
702 #define MA_CHANNEL_RIGHT MA_CHANNEL_FRONT_RIGHT
703 #define MA_CHANNEL_POSITION_COUNT MA_CHANNEL_AUX_31 + 1
704 
705 
706 typedef int ma_result;
707 #define MA_SUCCESS 0
708 
709 /* General errors. */
710 #define MA_ERROR -1 /* A generic error. */
711 #define MA_INVALID_ARGS -2
712 #define MA_INVALID_OPERATION -3
713 #define MA_OUT_OF_MEMORY -4
714 #define MA_ACCESS_DENIED -5
715 #define MA_TOO_LARGE -6
716 #define MA_TIMEOUT -7
717 
718 /* General miniaudio-specific errors. */
719 #define MA_FORMAT_NOT_SUPPORTED -100
720 #define MA_DEVICE_TYPE_NOT_SUPPORTED -101
721 #define MA_SHARE_MODE_NOT_SUPPORTED -102
722 #define MA_NO_BACKEND -103
723 #define MA_NO_DEVICE -104
724 #define MA_API_NOT_FOUND -105
725 #define MA_INVALID_DEVICE_CONFIG -106
726 
727 /* State errors. */
728 #define MA_DEVICE_BUSY -200
729 #define MA_DEVICE_NOT_INITIALIZED -201
730 #define MA_DEVICE_NOT_STARTED -202
731 #define MA_DEVICE_UNAVAILABLE -203
732 
733 /* Operation errors. */
734 #define MA_FAILED_TO_MAP_DEVICE_BUFFER -300
735 #define MA_FAILED_TO_UNMAP_DEVICE_BUFFER -301
736 #define MA_FAILED_TO_INIT_BACKEND -302
737 #define MA_FAILED_TO_READ_DATA_FROM_CLIENT -303
738 #define MA_FAILED_TO_READ_DATA_FROM_DEVICE -304
739 #define MA_FAILED_TO_SEND_DATA_TO_CLIENT -305
740 #define MA_FAILED_TO_SEND_DATA_TO_DEVICE -306
741 #define MA_FAILED_TO_OPEN_BACKEND_DEVICE -307
742 #define MA_FAILED_TO_START_BACKEND_DEVICE -308
743 #define MA_FAILED_TO_STOP_BACKEND_DEVICE -309
744 #define MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE -310
745 #define MA_FAILED_TO_CREATE_MUTEX -311
746 #define MA_FAILED_TO_CREATE_EVENT -312
747 #define MA_FAILED_TO_CREATE_THREAD -313
748 
749 
750 /* Standard sample rates. */
751 #define MA_SAMPLE_RATE_8000 8000
752 #define MA_SAMPLE_RATE_11025 11025
753 #define MA_SAMPLE_RATE_16000 16000
754 #define MA_SAMPLE_RATE_22050 22050
755 #define MA_SAMPLE_RATE_24000 24000
756 #define MA_SAMPLE_RATE_32000 32000
757 #define MA_SAMPLE_RATE_44100 44100
758 #define MA_SAMPLE_RATE_48000 48000
759 #define MA_SAMPLE_RATE_88200 88200
760 #define MA_SAMPLE_RATE_96000 96000
761 #define MA_SAMPLE_RATE_176400 176400
762 #define MA_SAMPLE_RATE_192000 192000
763 #define MA_SAMPLE_RATE_352800 352800
764 #define MA_SAMPLE_RATE_384000 384000
765 
766 #define MA_MIN_PCM_SAMPLE_SIZE_IN_BYTES 1 /* For simplicity, miniaudio does not support PCM samples that are not byte aligned. */
767 #define MA_MAX_PCM_SAMPLE_SIZE_IN_BYTES 8
768 #define MA_MIN_CHANNELS 1
769 #define MA_MAX_CHANNELS 32
770 #define MA_MIN_SAMPLE_RATE MA_SAMPLE_RATE_8000
771 #define MA_MAX_SAMPLE_RATE MA_SAMPLE_RATE_384000
772 #define MA_SRC_SINC_MIN_WINDOW_WIDTH 2
773 #define MA_SRC_SINC_MAX_WINDOW_WIDTH 32
774 #define MA_SRC_SINC_DEFAULT_WINDOW_WIDTH 32
775 #define MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION 8
776 #define MA_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES 256
777 
778 typedef enum
779 {
782 
783 typedef enum
784 {
788 
789 typedef enum
790 {
795 
796 typedef enum
797 {
798  /*
799  I like to keep these explicitly defined because they're used as a key into a lookup table. When items are
800  added to this, make sure there are no gaps and that they're added to the lookup table in ma_get_bytes_per_sample().
801  */
802  ma_format_unknown = 0, /* Mainly used for indicating an error, but also used as the default for the output format for decoders. */
804  ma_format_s16 = 2, /* Seems to be the most widely supported format. */
805  ma_format_s24 = 3, /* Tightly packed. 3 bytes per sample. */
810 
811 typedef enum
812 {
813  ma_channel_mix_mode_rectangular = 0, /* Simple averaging based on the plane(s) the channel is sitting on. */
814  ma_channel_mix_mode_simple, /* Drop excess channels; zeroed out extra channels. */
815  ma_channel_mix_mode_custom_weights, /* Use custom weights specified in ma_channel_router_config. */
819 
820 typedef enum
821 {
824  ma_standard_channel_map_rfc3551, /* Based off AIFF. */
827  ma_standard_channel_map_sound4, /* FreeBSD's sound(4). */
828  ma_standard_channel_map_sndio, /* www.sndio.org/tips.html */
829  ma_standard_channel_map_webaudio = ma_standard_channel_map_flac, /* https://webaudio.github.io/web-audio-api/#ChannelOrdering. Only 1, 2, 4 and 6 channels are defined, but can fill in the gaps with logical assumptions. */
832 
833 typedef enum
834 {
838 
839 
841 typedef ma_uint32 (* ma_format_converter_read_proc) (ma_format_converter* pConverter, ma_uint32 frameCount, void* pFramesOut, void* pUserData);
842 typedef ma_uint32 (* ma_format_converter_read_deinterleaved_proc)(ma_format_converter* pConverter, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData);
843 
844 typedef struct
845 {
858  void* pUserData;
860 
862 {
868  void (* onConvertPCM)(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode);
869  void (* onInterleavePCM)(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels);
870  void (* onDeinterleavePCM)(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels);
871 };
872 
873 
874 
875 typedef struct ma_channel_router ma_channel_router;
876 typedef ma_uint32 (* ma_channel_router_read_deinterleaved_proc)(ma_channel_router* pRouter, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData);
877 
878 typedef struct
879 {
883  ma_channel channelMapOut[MA_MAX_CHANNELS];
885  float weights[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
891  void* pUserData;
893 
895 {
906 };
907 
908 
909 
910 typedef struct ma_src ma_src;
911 typedef ma_uint32 (* ma_src_read_deinterleaved_proc)(ma_src* pSRC, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData); /* Returns the number of frames that were read. */
912 
913 typedef enum
914 {
920 
921 typedef enum
922 {
927 
928 typedef struct
929 {
933 
934 typedef struct
935 {
946  void* pUserData;
948 } ma_src_config;
949 
951 {
952  union
953  {
954  struct
955  {
957  float timeIn;
958  ma_uint32 leftoverFrames;
959  } linear;
960 
961  struct
962  {
964  float timeIn;
965  ma_uint32 inputFrameCount; /* The number of frames sitting in the input buffer, not including the first half of the window. */
966  ma_uint32 windowPosInSamples; /* An offset of <input>. */
967  float table[MA_SRC_SINC_MAX_WINDOW_WIDTH*1 * MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION]; /* Precomputed lookup table. The +1 is used to avoid the need for an overflow check. */
968  } sinc;
969  };
970 
972  ma_bool32 isEndOfInputLoaded : 1;
973  ma_bool32 useSSE2 : 1;
974  ma_bool32 useAVX2 : 1;
975  ma_bool32 useAVX512 : 1;
976  ma_bool32 useNEON : 1;
977 };
978 
979 typedef struct ma_pcm_converter ma_pcm_converter;
980 typedef ma_uint32 (* ma_pcm_converter_read_proc)(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint32 frameCount, void* pUserData);
981 
982 typedef struct
983 {
991  ma_channel channelMapOut[MA_MAX_CHANNELS];
996  ma_bool32 neverConsumeEndOfInput : 1; /* <-- For SRC. */
1002  void* pUserData;
1003  union
1004  {
1006  };
1008 
1010 {
1012  void* pUserData;
1013  ma_format_converter formatConverterIn; /* For converting data to f32 in preparation for further processing. */
1014  ma_format_converter formatConverterOut; /* For converting data to the requested output format. Used as the final step in the processing pipeline. */
1015  ma_channel_router channelRouter; /* For channel conversion. */
1016  ma_src src; /* For sample rate conversion. */
1017  ma_bool32 isDynamicSampleRateAllowed : 1; /* ma_pcm_converter_set_input_sample_rate() and ma_pcm_converter_set_output_sample_rate() will fail if this is set to false. */
1018  ma_bool32 isPreFormatConversionRequired : 1;
1019  ma_bool32 isPostFormatConversionRequired : 1;
1020  ma_bool32 isChannelRoutingRequired : 1;
1021  ma_bool32 isSRCRequired : 1;
1022  ma_bool32 isChannelRoutingAtStart : 1;
1023  ma_bool32 isPassthrough : 1; /* <-- Will be set to true when the conversion pipeline is an optimized passthrough. */
1024 };
1025 
1026 
1027 /************************************************************************************************************************************************************
1028 *************************************************************************************************************************************************************
1029 
1030 DATA CONVERSION
1031 ===============
1032 
1033 This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc.
1034 
1035 *************************************************************************************************************************************************************
1036 ************************************************************************************************************************************************************/
1037 
1038 /************************************************************************************************************************************************************
1039 
1040 Channel Maps
1041 ============
1042 
1043 Below is the channel map used by ma_standard_channel_map_default:
1044 
1045 |---------------|------------------------------|
1046 | Channel Count | Mapping |
1047 |---------------|------------------------------|
1048 | 1 (Mono) | 0: MA_CHANNEL_MONO |
1049 |---------------|------------------------------|
1050 | 2 (Stereo) | 0: MA_CHANNEL_FRONT_LEFT |
1051 | | 1: MA_CHANNEL_FRONT_RIGHT |
1052 |---------------|------------------------------|
1053 | 3 | 0: MA_CHANNEL_FRONT_LEFT |
1054 | | 1: MA_CHANNEL_FRONT_RIGHT |
1055 | | 2: MA_CHANNEL_FRONT_CENTER |
1056 |---------------|------------------------------|
1057 | 4 (Surround) | 0: MA_CHANNEL_FRONT_LEFT |
1058 | | 1: MA_CHANNEL_FRONT_RIGHT |
1059 | | 2: MA_CHANNEL_FRONT_CENTER |
1060 | | 3: MA_CHANNEL_BACK_CENTER |
1061 |---------------|------------------------------|
1062 | 5 | 0: MA_CHANNEL_FRONT_LEFT |
1063 | | 1: MA_CHANNEL_FRONT_RIGHT |
1064 | | 2: MA_CHANNEL_FRONT_CENTER |
1065 | | 3: MA_CHANNEL_BACK_LEFT |
1066 | | 4: MA_CHANNEL_BACK_RIGHT |
1067 |---------------|------------------------------|
1068 | 6 (5.1) | 0: MA_CHANNEL_FRONT_LEFT |
1069 | | 1: MA_CHANNEL_FRONT_RIGHT |
1070 | | 2: MA_CHANNEL_FRONT_CENTER |
1071 | | 3: MA_CHANNEL_LFE |
1072 | | 4: MA_CHANNEL_SIDE_LEFT |
1073 | | 5: MA_CHANNEL_SIDE_RIGHT |
1074 |---------------|------------------------------|
1075 | 7 | 0: MA_CHANNEL_FRONT_LEFT |
1076 | | 1: MA_CHANNEL_FRONT_RIGHT |
1077 | | 2: MA_CHANNEL_FRONT_CENTER |
1078 | | 3: MA_CHANNEL_LFE |
1079 | | 4: MA_CHANNEL_BACK_CENTER |
1080 | | 4: MA_CHANNEL_SIDE_LEFT |
1081 | | 5: MA_CHANNEL_SIDE_RIGHT |
1082 |---------------|------------------------------|
1083 | 8 (7.1) | 0: MA_CHANNEL_FRONT_LEFT |
1084 | | 1: MA_CHANNEL_FRONT_RIGHT |
1085 | | 2: MA_CHANNEL_FRONT_CENTER |
1086 | | 3: MA_CHANNEL_LFE |
1087 | | 4: MA_CHANNEL_BACK_LEFT |
1088 | | 5: MA_CHANNEL_BACK_RIGHT |
1089 | | 6: MA_CHANNEL_SIDE_LEFT |
1090 | | 7: MA_CHANNEL_SIDE_RIGHT |
1091 |---------------|------------------------------|
1092 | Other | All channels set to 0. This |
1093 | | is equivalent to the same |
1094 | | mapping as the device. |
1095 |---------------|------------------------------|
1096 
1097 ************************************************************************************************************************************************************/
1098 
1099 /*
1100 Helper for retrieving a standard channel map.
1101 */
1103 
1104 /*
1105 Copies a channel map.
1106 */
1107 void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels);
1108 
1109 
1110 /*
1111 Determines whether or not a channel map is valid.
1112 
1113 A blank channel map is valid (all channels set to MA_CHANNEL_NONE). The way a blank channel map is handled is context specific, but
1114 is usually treated as a passthrough.
1115 
1116 Invalid channel maps:
1117  - A channel map with no channels
1118  - A channel map with more than one channel and a mono channel
1119 */
1121 
1122 /*
1123 Helper for comparing two channel maps for equality.
1124 
1125 This assumes the channel count is the same between the two.
1126 */
1128 
1129 /*
1130 Helper for determining if a channel map is blank (all channels set to MA_CHANNEL_NONE).
1131 */
1133 
1134 /*
1135 Helper for determining whether or not a channel is present in the given channel map.
1136 */
1138 
1139 
1140 /************************************************************************************************************************************************************
1141 
1142 Format Conversion
1143 =================
1144 The format converter serves two purposes:
1145  1) Conversion between data formats (u8 to f32, etc.)
1146  2) Interleaving and deinterleaving
1147 
1148 When initializing a converter, you specify the input and output formats (u8, s16, etc.) and read callbacks. There are two read callbacks - one for
1149 interleaved input data (onRead) and another for deinterleaved input data (onReadDeinterleaved). You implement whichever is most convenient for you. You
1150 can implement both, but it's not recommended as it just introduces unnecessary complexity.
1151 
1152 To read data as interleaved samples, use ma_format_converter_read(). Otherwise use ma_format_converter_read_deinterleaved().
1153 
1154 Dithering
1155 ---------
1156 The format converter also supports dithering. Dithering can be set using ditherMode variable in the config, like so.
1157 
1158  pConfig->ditherMode = ma_dither_mode_rectangle;
1159 
1160 The different dithering modes include the following, in order of efficiency:
1161  - None: ma_dither_mode_none
1162  - Rectangle: ma_dither_mode_rectangle
1163  - Triangle: ma_dither_mode_triangle
1164 
1165 Note that even if the dither mode is set to something other than ma_dither_mode_none, it will be ignored for conversions where dithering is not needed.
1166 Dithering is available for the following conversions:
1167  - s16 -> u8
1168  - s24 -> u8
1169  - s32 -> u8
1170  - f32 -> u8
1171  - s24 -> s16
1172  - s32 -> s16
1173  - f32 -> s16
1174 
1175 Note that it is not an error to pass something other than ma_dither_mode_none for conversions where dither is not used. It will just be ignored.
1176 
1177 ************************************************************************************************************************************************************/
1178 
1179 /*
1180 Initializes a format converter.
1181 */
1183 
1184 /*
1185 Reads data from the format converter as interleaved channels.
1186 */
1187 ma_uint64 ma_format_converter_read(ma_format_converter* pConverter, ma_uint64 frameCount, void* pFramesOut, void* pUserData);
1188 
1189 /*
1190 Reads data from the format converter as deinterleaved channels.
1191 */
1192 ma_uint64 ma_format_converter_read_deinterleaved(ma_format_converter* pConverter, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData);
1193 
1194 /*
1195 Helper for initializing a format converter config.
1196 */
1200 
1201 
1202 
1203 /************************************************************************************************************************************************************
1204 
1205 Channel Routing
1206 ===============
1207 There are two main things you can do with the channel router:
1208  1) Rearrange channels
1209  2) Convert from one channel count to another
1210 
1211 Channel Rearrangement
1212 ---------------------
1213 A simple example of channel rearrangement may be swapping the left and right channels in a stereo stream. To do this you just pass in the same channel
1214 count for both the input and output with channel maps that contain the same channels (in a different order).
1215 
1216 Channel Conversion
1217 ------------------
1218 The channel router can also convert from one channel count to another, such as converting a 5.1 stream to stero. When changing the channel count, the
1219 router will first perform a 1:1 mapping of channel positions that are present in both the input and output channel maps. The second thing it will do
1220 is distribute the input mono channel (if any) across all output channels, excluding any None and LFE channels. If there is an output mono channel, all
1221 input channels will be averaged, excluding any None and LFE channels.
1222 
1223 The last case to consider is when a channel position in the input channel map is not present in the output channel map, and vice versa. In this case the
1224 channel router will perform a blend of other related channels to produce an audible channel. There are several blending modes.
1225  1) Simple
1226  Unmatched channels are silenced.
1227  2) Planar Blending
1228  Channels are blended based on a set of planes that each speaker emits audio from.
1229 
1230 Rectangular / Planar Blending
1231 -----------------------------
1232 In this mode, channel positions are associated with a set of planes where the channel conceptually emits audio from. An example is the front/left speaker.
1233 This speaker is positioned to the front of the listener, so you can think of it as emitting audio from the front plane. It is also positioned to the left
1234 of the listener so you can think of it as also emitting audio from the left plane. Now consider the (unrealistic) situation where the input channel map
1235 contains only the front/left channel position, but the output channel map contains both the front/left and front/center channel. When deciding on the audio
1236 data to send to the front/center speaker (which has no 1:1 mapping with an input channel) we need to use some logic based on our available input channel
1237 positions.
1238 
1239 As mentioned earlier, our front/left speaker is, conceptually speaking, emitting audio from the front _and_ the left planes. Similarly, the front/center
1240 speaker is emitting audio from _only_ the front plane. What these two channels have in common is that they are both emitting audio from the front plane.
1241 Thus, it makes sense that the front/center speaker should receive some contribution from the front/left channel. How much contribution depends on their
1242 planar relationship (thus the name of this blending technique).
1243 
1244 Because the front/left channel is emitting audio from two planes (front and left), you can think of it as though it's willing to dedicate 50% of it's total
1245 volume to each of it's planes (a channel position emitting from 1 plane would be willing to given 100% of it's total volume to that plane, and a channel
1246 position emitting from 3 planes would be willing to given 33% of it's total volume to each plane). Similarly, the front/center speaker is emitting audio
1247 from only one plane so you can think of it as though it's willing to _take_ 100% of it's volume from front plane emissions. Now, since the front/left
1248 channel is willing to _give_ 50% of it's total volume to the front plane, and the front/center speaker is willing to _take_ 100% of it's total volume
1249 from the front, you can imagine that 50% of the front/left speaker will be given to the front/center speaker.
1250 
1251 Usage
1252 -----
1253 To use the channel router you need to specify three things:
1254  1) The input channel count and channel map
1255  2) The output channel count and channel map
1256  3) The mixing mode to use in the case where a 1:1 mapping is unavailable
1257 
1258 Note that input and output data is always deinterleaved 32-bit floating point.
1259 
1260 Initialize the channel router with ma_channel_router_init(). You will need to pass in a config object which specifies the input and output configuration,
1261 mixing mode and a callback for sending data to the router. This callback will be called when input data needs to be sent to the router for processing. Note
1262 that the mixing mode is only used when a 1:1 mapping is unavailable. This includes the custom weights mode.
1263 
1264 Read data from the channel router with ma_channel_router_read_deinterleaved(). Output data is always 32-bit floating point.
1265 
1266 ************************************************************************************************************************************************************/
1267 
1268 /*
1269 Initializes a channel router where it is assumed that the input data is non-interleaved.
1270 */
1272 
1273 /*
1274 Reads data from the channel router as deinterleaved channels.
1275 */
1276 ma_uint64 ma_channel_router_read_deinterleaved(ma_channel_router* pRouter, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData);
1277 
1278 /*
1279 Helper for initializing a channel router config.
1280 */
1282 
1283 
1284 /************************************************************************************************************************************************************
1285 
1286 Sample Rate Conversion
1287 ======================
1288 
1289 ************************************************************************************************************************************************************/
1290 
1291 /*
1292 Initializes a sample rate conversion object.
1293 */
1295 
1296 /*
1297 Dynamically adjusts the sample rate.
1298 
1299 This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
1300 is not acceptable you will need to use your own algorithm.
1301 */
1302 ma_result ma_src_set_sample_rate(ma_src* pSRC, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
1303 
1304 /*
1305 Reads a number of frames.
1306 
1307 Returns the number of frames actually read.
1308 */
1309 ma_uint64 ma_src_read_deinterleaved(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData);
1310 
1311 /*
1312 Helper for creating a sample rate conversion config.
1313 */
1315 ma_src_config ma_src_config_init(ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_uint32 channels, ma_src_read_deinterleaved_proc onReadDeinterleaved, void* pUserData);
1316 
1317 
1318 /************************************************************************************************************************************************************
1319 
1320 Conversion
1321 
1322 ************************************************************************************************************************************************************/
1323 
1324 /*
1325 Initializes a DSP object.
1326 */
1328 
1329 /*
1330 Dynamically adjusts the input sample rate.
1331 
1332 This will fail is the DSP was not initialized with allowDynamicSampleRate.
1333 
1334 DEPRECATED. Use ma_pcm_converter_set_sample_rate() instead.
1335 */
1337 
1338 /*
1339 Dynamically adjusts the output sample rate.
1340 
1341 This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
1342 is not acceptable you will need to use your own algorithm.
1343 
1344 This will fail is the DSP was not initialized with allowDynamicSampleRate.
1345 
1346 DEPRECATED. Use ma_pcm_converter_set_sample_rate() instead.
1347 */
1349 
1350 /*
1351 Dynamically adjusts the output sample rate.
1352 
1353 This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
1354 is not acceptable you will need to use your own algorithm.
1355 
1356 This will fail if the DSP was not initialized with allowDynamicSampleRate.
1357 */
1359 
1360 /*
1361 Reads a number of frames and runs them through the DSP processor.
1362 */
1363 ma_uint64 ma_pcm_converter_read(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint64 frameCount);
1364 
1365 /*
1366 Helper for initializing a ma_pcm_converter_config object.
1367 */
1369 ma_pcm_converter_config ma_pcm_converter_config_init(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_pcm_converter_read_proc onRead, void* pUserData);
1370 ma_pcm_converter_config ma_pcm_converter_config_init_ex(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_channel channelMapIn[MA_MAX_CHANNELS], ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_channel channelMapOut[MA_MAX_CHANNELS], ma_pcm_converter_read_proc onRead, void* pUserData);
1371 
1372 /*
1373 High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to
1374 determine the required size of the output buffer.
1375 
1376 A return value of 0 indicates an error.
1377 
1378 This function is useful for one-off bulk conversions, but if you're streaming data you should use the ma_pcm_converter APIs instead.
1379 */
1380 ma_uint64 ma_convert_frames(void* pOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_uint64 frameCount);
1381 ma_uint64 ma_convert_frames_ex(void* pOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_channel channelMapOut[MA_MAX_CHANNELS], const void* pIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_channel channelMapIn[MA_MAX_CHANNELS], ma_uint64 frameCount);
1382 
1383 
1384 /************************************************************************************************************************************************************
1385 
1386 Ring Buffer
1387 ===========
1388 
1389 Features
1390 --------
1391 - Lock free (assuming single producer, single consumer)
1392 - Support for interleaved and deinterleaved streams
1393 - Allows the caller to allocate their own block of memory
1394 
1395 Usage
1396 -----
1397 - Call ma_rb_init() to initialize a simple buffer, with an optional pre-allocated buffer. If you pass in NULL
1398  for the pre-allocated buffer, it will be allocated for you and free()'d in ma_rb_uninit(). If you pass in
1399  your own pre-allocated buffer, free()-ing is left to you.
1400 
1401 - Call ma_rb_init_ex() if you need a deinterleaved buffer. The data for each sub-buffer is offset from each
1402  other based on the stride. Use ma_rb_get_subbuffer_stride(), ma_rb_get_subbuffer_offset() and
1403  ma_rb_get_subbuffer_ptr() to manage your sub-buffers.
1404 
1405 - Use ma_rb_acquire_read() and ma_rb_acquire_write() to retrieve a pointer to a section of the ring buffer.
1406  You specify the number of bytes you need, and on output it will set to what was actually acquired. If the
1407  read or write pointer is positioned such that the number of bytes requested will require a loop, it will be
1408  clamped to the end of the buffer. Therefore, the number of bytes you're given may be less than the number
1409  you requested.
1410 
1411 - After calling ma_rb_acquire_read/write(), you do your work on the buffer and then "commit" it with
1412  ma_rb_commit_read/write(). This is where the read/write pointers are updated. When you commit you need to
1413  pass in the buffer that was returned by the earlier call to ma_rb_acquire_read/write() and is only used
1414  for validation. The number of bytes passed to ma_rb_commit_read/write() is what's used to increment the
1415  pointers.
1416 
1417 - If you want to correct for drift between the write pointer and the read pointer you can use a combination
1418  of ma_rb_pointer_distance(), ma_rb_seek_read() and ma_rb_seek_write(). Note that you can only move the
1419  pointers forward, and you should only move the read pointer forward via the consumer thread, and the write
1420  pointer forward by the producer thread. If there is too much space between the pointers, move the read
1421  pointer forward. If there is too little space between the pointers, move the write pointer forward.
1422 
1423 
1424 Notes
1425 -----
1426 - Thread safety depends on a single producer, single consumer model. Only one thread is allowed to write, and
1427  only one thread is allowed to read. The producer is the only one allowed to move the write pointer, and the
1428  consumer is the only one allowed to move the read pointer.
1429 - Operates on bytes. Use ma_pcm_rb to operate in terms of PCM frames.
1430 - Maximum buffer size in bytes is 0x7FFFFFFF-(MA_SIMD_ALIGNMENT-1) because of reasons.
1431 
1432 
1433 PCM Ring Buffer
1434 ===============
1435 This is the same as the regular ring buffer, except that it works on PCM frames instead of bytes.
1436 
1437 ************************************************************************************************************************************************************/
1438 typedef struct
1439 {
1440  void* pBuffer;
1441  ma_uint32 subbufferSizeInBytes;
1442  ma_uint32 subbufferCount;
1443  ma_uint32 subbufferStrideInBytes;
1444  volatile ma_uint32 encodedReadOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. */
1445  volatile ma_uint32 encodedWriteOffset; /* Most significant bit is the loop flag. Lower 31 bits contains the actual offset in bytes. */
1446  ma_bool32 ownsBuffer : 1; /* Used to know whether or not miniaudio is responsible for free()-ing the buffer. */
1447  ma_bool32 clearOnWriteAcquire : 1; /* When set, clears the acquired write buffer before returning from ma_rb_acquire_write(). */
1448 } ma_rb;
1449 
1450 ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, ma_rb* pRB);
1451 ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, ma_rb* pRB);
1452 void ma_rb_uninit(ma_rb* pRB);
1453 void ma_rb_reset(ma_rb* pRB);
1454 ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
1455 ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut);
1456 ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
1457 ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut);
1458 ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes);
1459 ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes);
1460 ma_int32 ma_rb_pointer_distance(ma_rb* pRB); /* Returns the distance between the write pointer and the read pointer. Should never be negative for a correct program. Will return the number of bytes that can be read before the read pointer hits the write pointer. */
1465 size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex);
1466 void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer);
1467 
1468 
1469 typedef struct
1470 {
1474 } ma_pcm_rb;
1475 
1476 ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, ma_pcm_rb* pRB);
1477 ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, ma_pcm_rb* pRB);
1480 ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
1481 ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut);
1482 ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
1483 ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut);
1486 ma_int32 ma_pcm_rb_pointer_disance(ma_pcm_rb* pRB); /* Return value is in frames. */
1492 void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer);
1495 /************************************************************************************************************************************************************
1497 Miscellaneous Helpers
1499 ************************************************************************************************************************************************************/
1501 /*
1502 malloc(). Calls MA_MALLOC().
1503 */
1504 void* ma_malloc(size_t sz);
1505 
1506 /*
1507 realloc(). Calls MA_REALLOC().
1508 */
1509 void* ma_realloc(void* p, size_t sz);
1510 
1511 /*
1512 free(). Calls MA_FREE().
1513 */
1514 void ma_free(void* p);
1515 
1516 /*
1517 Performs an aligned malloc, with the assumption that the alignment is a power of 2.
1518 */
1519 void* ma_aligned_malloc(size_t sz, size_t alignment);
1520 
1521 /*
1522 Free's an aligned malloc'd buffer.
1523 */
1524 void ma_aligned_free(void* p);
1525 
1526 /*
1527 Retrieves a friendly name for a format.
1528 */
1530 
1531 /*
1532 Blends two frames in floating point format.
1533 */
1534 void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels);
1535 
1536 /*
1537 Retrieves the size of a sample in bytes for the given format.
1538 
1539 This API is efficient and is implemented using a lookup table.
1540 
1541 Thread Safety: SAFE
1542  This API is pure.
1543 */
1545 static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; }
1546 
1547 /*
1548 Converts a log level to a string.
1549 */
1550 const char* ma_log_level_to_string(ma_uint32 logLevel);
1551 
1552 
1553 /************************************************************************************************************************************************************
1554 
1555 Format Conversion
1556 
1557 ************************************************************************************************************************************************************/
1558 void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1559 void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1560 void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1561 void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1562 void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1563 void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1564 void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1565 void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1566 void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1567 void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1568 void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1569 void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1570 void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1571 void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1572 void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1573 void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1574 void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1575 void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1576 void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1577 void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
1578 void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode);
1579 
1580 /*
1581 Deinterleaves an interleaved buffer.
1582 */
1583 void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
1584 
1585 /*
1586 Interleaves a group of deinterleaved buffers.
1587 */
1588 void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
1589 
1590 
1591 /************************************************************************************************************************************************************
1592 *************************************************************************************************************************************************************
1593 
1594 DEVICE I/O
1595 ==========
1596 
1597 This section contains the APIs for device playback and capture. Here is where you'll find ma_device_init(), etc.
1598 
1599 *************************************************************************************************************************************************************
1600 ************************************************************************************************************************************************************/
1601 #ifndef MA_NO_DEVICE_IO
1602 /* Some backends are only supported on certain platforms. */
1603 #if defined(MA_WIN32)
1604  #define MA_SUPPORT_WASAPI
1605  #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */
1606  #define MA_SUPPORT_DSOUND
1607  #define MA_SUPPORT_WINMM
1608  #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */
1609  #endif
1610 #endif
1611 #if defined(MA_UNIX)
1612  #if defined(MA_LINUX)
1613  #if !defined(MA_ANDROID) /* ALSA is not supported on Android. */
1614  #define MA_SUPPORT_ALSA
1615  #endif
1616  #endif
1617  #if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN)
1618  #define MA_SUPPORT_PULSEAUDIO
1619  #define MA_SUPPORT_JACK
1620  #endif
1621  #if defined(MA_ANDROID)
1622  #define MA_SUPPORT_AAUDIO
1623  #define MA_SUPPORT_OPENSL
1624  #endif
1625  #if defined(__OpenBSD__) /* <-- Change this to "#if defined(MA_BSD)" to enable sndio on all BSD flavors. */
1626  #define MA_SUPPORT_SNDIO /* sndio is only supported on OpenBSD for now. May be expanded later if there's demand. */
1627  #endif
1628  #if defined(__NetBSD__) || defined(__OpenBSD__)
1629  #define MA_SUPPORT_AUDIO4 /* Only support audio(4) on platforms with known support. */
1630  #endif
1631  #if defined(__FreeBSD__) || defined(__DragonFly__)
1632  #define MA_SUPPORT_OSS /* Only support OSS on specific platforms with known support. */
1633  #endif
1634 #endif
1635 #if defined(MA_APPLE)
1636  #define MA_SUPPORT_COREAUDIO
1637 #endif
1638 #if defined(MA_EMSCRIPTEN)
1639  #define MA_SUPPORT_WEBAUDIO
1640 #endif
1641 
1642 /* Explicitly disable the Null backend for Emscripten because it uses a background thread which is not properly supported right now. */
1643 #if !defined(MA_EMSCRIPTEN)
1644 #define MA_SUPPORT_NULL
1645 #endif
1646 
1647 
1648 #if !defined(MA_NO_WASAPI) && defined(MA_SUPPORT_WASAPI)
1649  #define MA_ENABLE_WASAPI
1650 #endif
1651 #if !defined(MA_NO_DSOUND) && defined(MA_SUPPORT_DSOUND)
1652  #define MA_ENABLE_DSOUND
1653 #endif
1654 #if !defined(MA_NO_WINMM) && defined(MA_SUPPORT_WINMM)
1655  #define MA_ENABLE_WINMM
1656 #endif
1657 #if !defined(MA_NO_ALSA) && defined(MA_SUPPORT_ALSA)
1658  #define MA_ENABLE_ALSA
1659 #endif
1660 #if !defined(MA_NO_PULSEAUDIO) && defined(MA_SUPPORT_PULSEAUDIO)
1661  #define MA_ENABLE_PULSEAUDIO
1662 #endif
1663 #if !defined(MA_NO_JACK) && defined(MA_SUPPORT_JACK)
1664  #define MA_ENABLE_JACK
1665 #endif
1666 #if !defined(MA_NO_COREAUDIO) && defined(MA_SUPPORT_COREAUDIO)
1667  #define MA_ENABLE_COREAUDIO
1668 #endif
1669 #if !defined(MA_NO_SNDIO) && defined(MA_SUPPORT_SNDIO)
1670  #define MA_ENABLE_SNDIO
1671 #endif
1672 #if !defined(MA_NO_AUDIO4) && defined(MA_SUPPORT_AUDIO4)
1673  #define MA_ENABLE_AUDIO4
1674 #endif
1675 #if !defined(MA_NO_OSS) && defined(MA_SUPPORT_OSS)
1676  #define MA_ENABLE_OSS
1677 #endif
1678 #if !defined(MA_NO_AAUDIO) && defined(MA_SUPPORT_AAUDIO)
1679  #define MA_ENABLE_AAUDIO
1680 #endif
1681 #if !defined(MA_NO_OPENSL) && defined(MA_SUPPORT_OPENSL)
1682  #define MA_ENABLE_OPENSL
1683 #endif
1684 #if !defined(MA_NO_WEBAUDIO) && defined(MA_SUPPORT_WEBAUDIO)
1685  #define MA_ENABLE_WEBAUDIO
1686 #endif
1687 #if !defined(MA_NO_NULL) && defined(MA_SUPPORT_NULL)
1688  #define MA_ENABLE_NULL
1689 #endif
1690 
1691 #ifdef MA_SUPPORT_WASAPI
1692 /* We need a IMMNotificationClient object for WASAPI. */
1693 typedef struct
1694 {
1695  void* lpVtbl;
1697  ma_device* pDevice;
1698 } ma_IMMNotificationClient;
1699 #endif
1700 
1701 /* Backend enums must be in priority order. */
1702 typedef enum
1703 {
1717  ma_backend_null /* <-- Must always be the last item. Lowest priority, and used as the terminator for backend enumeration. */
1719 
1720 /* Thread priorties should be ordered such that the default priority of the worker thread is 0. */
1721 typedef enum
1722 {
1732 
1733 typedef struct
1734 {
1736 
1737  union
1738  {
1739 #ifdef MA_WIN32
1740  struct
1741  {
1742  /*HANDLE*/ ma_handle hThread;
1743  } win32;
1744 #endif
1745 #ifdef MA_POSIX
1746  struct
1747  {
1748  pthread_t thread;
1749  } posix;
1750 #endif
1751  int _unused;
1752  };
1753 } ma_thread;
1754 
1755 typedef struct
1756 {
1758 
1759  union
1760  {
1761 #ifdef MA_WIN32
1762  struct
1763  {
1764  /*HANDLE*/ ma_handle hMutex;
1765  } win32;
1766 #endif
1767 #ifdef MA_POSIX
1768  struct
1769  {
1770  pthread_mutex_t mutex;
1771  } posix;
1772 #endif
1773  int _unused;
1774  };
1775 } ma_mutex;
1776 
1777 typedef struct
1778 {
1780 
1781  union
1782  {
1783 #ifdef MA_WIN32
1784  struct
1785  {
1786  /*HANDLE*/ ma_handle hEvent;
1787  } win32;
1788 #endif
1789 #ifdef MA_POSIX
1790  struct
1791  {
1792  pthread_mutex_t mutex;
1793  pthread_cond_t condition;
1795  } posix;
1796 #endif
1797  int _unused;
1798  };
1799 } ma_event;
1800 
1801 
1802 /*
1803 The callback for processing audio data from the device.
1804 
1805 pOutput is a pointer to a buffer that will receive audio data that will later be played back through the speakers. This will be non-null
1806 for a playback or full-duplex device and null for a capture device.
1807 
1808 pInput is a pointer to a buffer containing input data from the device. This will be non-null for a capture or full-duplex device, and
1809 null for a playback device.
1810 
1811 frameCount is the number of PCM frames to process. If an output buffer is provided (pOutput is not null), applications should write out
1812 to the entire output buffer. Note that frameCount will not necessarily be exactly what you asked for when you initialized the deviced.
1813 The bufferSizeInFrames and bufferSizeInMilliseconds members of the device config are just hints, and are not necessarily exactly what
1814 you'll get.
1815 
1816 Do _not_ call any miniaudio APIs from the callback. Attempting the stop the device can result in a deadlock. The proper way to stop the
1817 device is to call ma_device_stop() from a different thread, normally the main application thread.
1818 */
1819 typedef void (* ma_device_callback_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
1820 
1821 /*
1822 The callback for when the device has been stopped.
1823 
1824 This will be called when the device is stopped explicitly with ma_device_stop() and also called implicitly when the device is stopped
1825 through external forces such as being unplugged or an internal error occuring.
1826 
1827 Do not restart the device from the callback.
1828 */
1829 typedef void (* ma_stop_proc)(ma_device* pDevice);
1830 
1831 /*
1832 The callback for handling log messages.
1833 
1834 It is possible for pDevice to be null in which case the log originated from the context. If it is non-null you can assume the message
1835 came from the device.
1836 
1837 logLevel is one of the following:
1838  MA_LOG_LEVEL_VERBOSE
1839  MA_LOG_LEVEL_INFO
1840  MA_LOG_LEVEL_WARNING
1841  MA_LOG_LEVEL_ERROR
1842 */
1843 typedef void (* ma_log_proc)(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message);
1844 
1845 typedef enum
1846 {
1852 
1853 typedef enum
1854 {
1858 
1859 typedef union
1860 {
1861 #ifdef MA_SUPPORT_WASAPI
1862  wchar_t wasapi[64]; /* WASAPI uses a wchar_t string for identification. */
1863 #endif
1864 #ifdef MA_SUPPORT_DSOUND
1865  ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */
1866 #endif
1867 #ifdef MA_SUPPORT_WINMM
1868  /*UINT_PTR*/ ma_uint32 winmm; /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */
1869 #endif
1870 #ifdef MA_SUPPORT_ALSA
1871  char alsa[256]; /* ALSA uses a name string for identification. */
1872 #endif
1873 #ifdef MA_SUPPORT_PULSEAUDIO
1874  char pulse[256]; /* PulseAudio uses a name string for identification. */
1875 #endif
1876 #ifdef MA_SUPPORT_JACK
1877  int jack; /* JACK always uses default devices. */
1878 #endif
1879 #ifdef MA_SUPPORT_COREAUDIO
1880  char coreaudio[256]; /* Core Audio uses a string for identification. */
1881 #endif
1882 #ifdef MA_SUPPORT_SNDIO
1883  char sndio[256]; /* "snd/0", etc. */
1884 #endif
1885 #ifdef MA_SUPPORT_AUDIO4
1886  char audio4[256]; /* "/dev/audio", etc. */
1887 #endif
1888 #ifdef MA_SUPPORT_OSS
1889  char oss[64]; /* "dev/dsp0", etc. "dev/dsp" for the default device. */
1890 #endif
1891 #ifdef MA_SUPPORT_AAUDIO
1892  ma_int32 aaudio; /* AAudio uses a 32-bit integer for identification. */
1893 #endif
1894 #ifdef MA_SUPPORT_OPENSL
1895  ma_uint32 opensl; /* OpenSL|ES uses a 32-bit unsigned integer for identification. */
1896 #endif
1897 #ifdef MA_SUPPORT_WEBAUDIO
1898  char webaudio[32]; /* Web Audio always uses default devices for now, but if this changes it'll be a GUID. */
1899 #endif
1900 #ifdef MA_SUPPORT_NULL
1901  int nullbackend; /* The null backend uses an integer for device IDs. */
1902 #endif
1903 } ma_device_id;
1904 
1905 typedef struct
1906 {
1907  /* Basic info. This is the only information guaranteed to be filled in during device enumeration. */
1909  char name[256];
1910 
1911  /*
1912  Detailed info. As much of this is filled as possible with ma_context_get_device_info(). Note that you are allowed to initialize
1913  a device with settings outside of this range, but it just means the data will be converted using miniaudio's data conversion
1914  pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided
1915  here mainly for informational purposes or in the rare case that someone might find it useful.
1916 
1917  These will be set to 0 when returned by ma_context_enumerate_devices() or ma_context_get_devices().
1918  */
1925 } ma_device_info;
1926 
1927 typedef union
1928 {
1930  double counterD;
1931 } ma_timer;
1932 
1933 typedef struct
1934 {
1941  ma_bool32 noPreZeroedOutputBuffer; /* When set to true, the contents of the output buffer passed into the data callback will be left undefined rather than initialized to zero. */
1942  ma_bool32 noClip; /* When set to true, the contents of the output buffer passed into the data callback will be clipped after returning. Only applies when the playback sample format is f32. */
1945  void* pUserData;
1946  struct
1947  {
1953  } playback;
1954  struct
1955  {
1956  ma_device_id* pDeviceID;
1957  ma_format format;
1958  ma_uint32 channels;
1959  ma_channel channelMap[MA_MAX_CHANNELS];
1960  ma_share_mode shareMode;
1961  } capture;
1962 
1963  struct
1964  {
1965  ma_bool32 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
1966  ma_bool32 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
1967  } wasapi;
1968  struct
1969  {
1970  ma_bool32 noMMap; /* Disables MMap mode. */
1971  } alsa;
1972  struct
1973  {
1974  const char* pStreamNamePlayback;
1975  const char* pStreamNameCapture;
1976  } pulse;
1978 
1979 typedef struct
1980 {
1983  void* pUserData;
1984 
1985  struct
1986  {
1988  } alsa;
1989  struct
1990  {
1991  const char* pApplicationName;
1992  const char* pServerName;
1993  ma_bool32 tryAutoSpawn; /* Enables autospawning of the PulseAudio daemon if necessary. */
1994  } pulse;
1995  struct
1996  {
1997  const char* pClientName;
1999  } jack;
2001 
2002 typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData);
2003 
2005 {
2006  ma_backend backend; /* DirectSound, ALSA, etc. */
2009  void* pUserData;
2010  ma_mutex deviceEnumLock; /* Used to make ma_context_get_devices() thread safe. */
2011  ma_mutex deviceInfoLock; /* Used to make ma_context_get_device_info() thread safe. */
2012  ma_uint32 deviceInfoCapacity; /* Total capacity of pDeviceInfos. */
2015  ma_device_info* pDeviceInfos; /* Playback devices first, then capture. */
2016  ma_bool32 isBackendAsynchronous : 1; /* Set when the context is initialized. Set to 1 for asynchronous backends such as Core Audio and JACK. Do not modify. */
2017 
2018  ma_result (* onUninit )(ma_context* pContext);
2019  ma_bool32 (* onDeviceIDEqual )(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1);
2020  ma_result (* onEnumDevices )(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData); /* Return false from the callback to stop enumeration. */
2021  ma_result (* onGetDeviceInfo )(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo);
2022  ma_result (* onDeviceInit )(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice);
2027 
2028  union
2029  {
2030 #ifdef MA_SUPPORT_WASAPI
2031  struct
2032  {
2033  int _unused;
2034  } wasapi;
2035 #endif
2036 #ifdef MA_SUPPORT_DSOUND
2037  struct
2038  {
2039  ma_handle hDSoundDLL;
2040  ma_proc DirectSoundCreate;
2041  ma_proc DirectSoundEnumerateA;
2042  ma_proc DirectSoundCaptureCreate;
2043  ma_proc DirectSoundCaptureEnumerateA;
2044  } dsound;
2045 #endif
2046 #ifdef MA_SUPPORT_WINMM
2047  struct
2048  {
2049  ma_handle hWinMM;
2050  ma_proc waveOutGetNumDevs;
2051  ma_proc waveOutGetDevCapsA;
2052  ma_proc waveOutOpen;
2053  ma_proc waveOutClose;
2054  ma_proc waveOutPrepareHeader;
2055  ma_proc waveOutUnprepareHeader;
2056  ma_proc waveOutWrite;
2057  ma_proc waveOutReset;
2058  ma_proc waveInGetNumDevs;
2059  ma_proc waveInGetDevCapsA;
2060  ma_proc waveInOpen;
2061  ma_proc waveInClose;
2062  ma_proc waveInPrepareHeader;
2063  ma_proc waveInUnprepareHeader;
2064  ma_proc waveInAddBuffer;
2065  ma_proc waveInStart;
2066  ma_proc waveInReset;
2067  } winmm;
2068 #endif
2069 #ifdef MA_SUPPORT_ALSA
2070  struct
2071  {
2072  ma_handle asoundSO;
2073  ma_proc snd_pcm_open;
2074  ma_proc snd_pcm_close;
2075  ma_proc snd_pcm_hw_params_sizeof;
2076  ma_proc snd_pcm_hw_params_any;
2077  ma_proc snd_pcm_hw_params_set_format;
2078  ma_proc snd_pcm_hw_params_set_format_first;
2079  ma_proc snd_pcm_hw_params_get_format_mask;
2080  ma_proc snd_pcm_hw_params_set_channels_near;
2081  ma_proc snd_pcm_hw_params_set_rate_resample;
2082  ma_proc snd_pcm_hw_params_set_rate_near;
2083  ma_proc snd_pcm_hw_params_set_buffer_size_near;
2084  ma_proc snd_pcm_hw_params_set_periods_near;
2085  ma_proc snd_pcm_hw_params_set_access;
2086  ma_proc snd_pcm_hw_params_get_format;
2087  ma_proc snd_pcm_hw_params_get_channels;
2088  ma_proc snd_pcm_hw_params_get_channels_min;
2089  ma_proc snd_pcm_hw_params_get_channels_max;
2090  ma_proc snd_pcm_hw_params_get_rate;
2091  ma_proc snd_pcm_hw_params_get_rate_min;
2092  ma_proc snd_pcm_hw_params_get_rate_max;
2093  ma_proc snd_pcm_hw_params_get_buffer_size;
2094  ma_proc snd_pcm_hw_params_get_periods;
2095  ma_proc snd_pcm_hw_params_get_access;
2096  ma_proc snd_pcm_hw_params;
2097  ma_proc snd_pcm_sw_params_sizeof;
2098  ma_proc snd_pcm_sw_params_current;
2099  ma_proc snd_pcm_sw_params_get_boundary;
2100  ma_proc snd_pcm_sw_params_set_avail_min;
2101  ma_proc snd_pcm_sw_params_set_start_threshold;
2102  ma_proc snd_pcm_sw_params_set_stop_threshold;
2103  ma_proc snd_pcm_sw_params;
2104  ma_proc snd_pcm_format_mask_sizeof;
2105  ma_proc snd_pcm_format_mask_test;
2106  ma_proc snd_pcm_get_chmap;
2107  ma_proc snd_pcm_state;
2108  ma_proc snd_pcm_prepare;
2109  ma_proc snd_pcm_start;
2110  ma_proc snd_pcm_drop;
2111  ma_proc snd_pcm_drain;
2112  ma_proc snd_device_name_hint;
2113  ma_proc snd_device_name_get_hint;
2114  ma_proc snd_card_get_index;
2115  ma_proc snd_device_name_free_hint;
2116  ma_proc snd_pcm_mmap_begin;
2117  ma_proc snd_pcm_mmap_commit;
2118  ma_proc snd_pcm_recover;
2119  ma_proc snd_pcm_readi;
2120  ma_proc snd_pcm_writei;
2121  ma_proc snd_pcm_avail;
2122  ma_proc snd_pcm_avail_update;
2123  ma_proc snd_pcm_wait;
2124  ma_proc snd_pcm_info;
2125  ma_proc snd_pcm_info_sizeof;
2126  ma_proc snd_pcm_info_get_name;
2127  ma_proc snd_config_update_free_global;
2128 
2129  ma_mutex internalDeviceEnumLock;
2130  ma_bool32 useVerboseDeviceEnumeration;
2131  } alsa;
2132 #endif
2133 #ifdef MA_SUPPORT_PULSEAUDIO
2134  struct
2135  {
2136  ma_handle pulseSO;
2137  ma_proc pa_mainloop_new;
2138  ma_proc pa_mainloop_free;
2139  ma_proc pa_mainloop_get_api;
2140  ma_proc pa_mainloop_iterate;
2141  ma_proc pa_mainloop_wakeup;
2142  ma_proc pa_context_new;
2143  ma_proc pa_context_unref;
2144  ma_proc pa_context_connect;
2145  ma_proc pa_context_disconnect;
2146  ma_proc pa_context_set_state_callback;
2147  ma_proc pa_context_get_state;
2148  ma_proc pa_context_get_sink_info_list;
2149  ma_proc pa_context_get_source_info_list;
2150  ma_proc pa_context_get_sink_info_by_name;
2151  ma_proc pa_context_get_source_info_by_name;
2152  ma_proc pa_operation_unref;
2153  ma_proc pa_operation_get_state;
2154  ma_proc pa_channel_map_init_extend;
2155  ma_proc pa_channel_map_valid;
2156  ma_proc pa_channel_map_compatible;
2157  ma_proc pa_stream_new;
2158  ma_proc pa_stream_unref;
2159  ma_proc pa_stream_connect_playback;
2160  ma_proc pa_stream_connect_record;
2161  ma_proc pa_stream_disconnect;
2162  ma_proc pa_stream_get_state;
2163  ma_proc pa_stream_get_sample_spec;
2164  ma_proc pa_stream_get_channel_map;
2165  ma_proc pa_stream_get_buffer_attr;
2166  ma_proc pa_stream_set_buffer_attr;
2167  ma_proc pa_stream_get_device_name;
2168  ma_proc pa_stream_set_write_callback;
2169  ma_proc pa_stream_set_read_callback;
2170  ma_proc pa_stream_flush;
2171  ma_proc pa_stream_drain;
2172  ma_proc pa_stream_is_corked;
2173  ma_proc pa_stream_cork;
2174  ma_proc pa_stream_trigger;
2175  ma_proc pa_stream_begin_write;
2176  ma_proc pa_stream_write;
2177  ma_proc pa_stream_peek;
2178  ma_proc pa_stream_drop;
2179  ma_proc pa_stream_writable_size;
2180  ma_proc pa_stream_readable_size;
2181 
2182  char* pApplicationName;
2183  char* pServerName;
2184  ma_bool32 tryAutoSpawn;
2185  } pulse;
2186 #endif
2187 #ifdef MA_SUPPORT_JACK
2188  struct
2189  {
2190  ma_handle jackSO;
2191  ma_proc jack_client_open;
2192  ma_proc jack_client_close;
2193  ma_proc jack_client_name_size;
2194  ma_proc jack_set_process_callback;
2195  ma_proc jack_set_buffer_size_callback;
2196  ma_proc jack_on_shutdown;
2197  ma_proc jack_get_sample_rate;
2198  ma_proc jack_get_buffer_size;
2199  ma_proc jack_get_ports;
2200  ma_proc jack_activate;
2201  ma_proc jack_deactivate;
2202  ma_proc jack_connect;
2203  ma_proc jack_port_register;
2204  ma_proc jack_port_name;
2205  ma_proc jack_port_get_buffer;
2206  ma_proc jack_free;
2207 
2208  char* pClientName;
2209  ma_bool32 tryStartServer;
2210  } jack;
2211 #endif
2212 #ifdef MA_SUPPORT_COREAUDIO
2213  struct
2214  {
2215  ma_handle hCoreFoundation;
2216  ma_proc CFStringGetCString;
2217  ma_proc CFRelease;
2218 
2219  ma_handle hCoreAudio;
2220  ma_proc AudioObjectGetPropertyData;
2221  ma_proc AudioObjectGetPropertyDataSize;
2222  ma_proc AudioObjectSetPropertyData;
2223  ma_proc AudioObjectAddPropertyListener;
2224  ma_proc AudioObjectRemovePropertyListener;
2225 
2226  ma_handle hAudioUnit; /* Could possibly be set to AudioToolbox on later versions of macOS. */
2227  ma_proc AudioComponentFindNext;
2228  ma_proc AudioComponentInstanceDispose;
2229  ma_proc AudioComponentInstanceNew;
2230  ma_proc AudioOutputUnitStart;
2231  ma_proc AudioOutputUnitStop;
2232  ma_proc AudioUnitAddPropertyListener;
2233  ma_proc AudioUnitGetPropertyInfo;
2234  ma_proc AudioUnitGetProperty;
2235  ma_proc AudioUnitSetProperty;
2236  ma_proc AudioUnitInitialize;
2237  ma_proc AudioUnitRender;
2238 
2239  /*AudioComponent*/ ma_ptr component;
2240  } coreaudio;
2241 #endif
2242 #ifdef MA_SUPPORT_SNDIO
2243  struct
2244  {
2245  ma_handle sndioSO;
2246  ma_proc sio_open;
2247  ma_proc sio_close;
2248  ma_proc sio_setpar;
2249  ma_proc sio_getpar;
2250  ma_proc sio_getcap;
2251  ma_proc sio_start;
2252  ma_proc sio_stop;
2253  ma_proc sio_read;
2254  ma_proc sio_write;
2255  ma_proc sio_onmove;
2256  ma_proc sio_nfds;
2257  ma_proc sio_pollfd;
2258  ma_proc sio_revents;
2259  ma_proc sio_eof;
2260  ma_proc sio_setvol;
2261  ma_proc sio_onvol;
2262  ma_proc sio_initpar;
2263  } sndio;
2264 #endif
2265 #ifdef MA_SUPPORT_AUDIO4
2266  struct
2267  {
2268  int _unused;
2269  } audio4;
2270 #endif
2271 #ifdef MA_SUPPORT_OSS
2272  struct
2273  {
2274  int versionMajor;
2275  int versionMinor;
2276  } oss;
2277 #endif
2278 #ifdef MA_SUPPORT_AAUDIO
2279  struct
2280  {
2281  ma_handle hAAudio; /* libaaudio.so */
2282  ma_proc AAudio_createStreamBuilder;
2283  ma_proc AAudioStreamBuilder_delete;
2284  ma_proc AAudioStreamBuilder_setDeviceId;
2285  ma_proc AAudioStreamBuilder_setDirection;
2286  ma_proc AAudioStreamBuilder_setSharingMode;
2287  ma_proc AAudioStreamBuilder_setFormat;
2288  ma_proc AAudioStreamBuilder_setChannelCount;
2289  ma_proc AAudioStreamBuilder_setSampleRate;
2290  ma_proc AAudioStreamBuilder_setBufferCapacityInFrames;
2291  ma_proc AAudioStreamBuilder_setFramesPerDataCallback;
2292  ma_proc AAudioStreamBuilder_setDataCallback;
2293  ma_proc AAudioStreamBuilder_setPerformanceMode;
2294  ma_proc AAudioStreamBuilder_openStream;
2295  ma_proc AAudioStream_close;
2296  ma_proc AAudioStream_getState;
2297  ma_proc AAudioStream_waitForStateChange;
2298  ma_proc AAudioStream_getFormat;
2299  ma_proc AAudioStream_getChannelCount;
2300  ma_proc AAudioStream_getSampleRate;
2301  ma_proc AAudioStream_getBufferCapacityInFrames;
2302  ma_proc AAudioStream_getFramesPerDataCallback;
2303  ma_proc AAudioStream_getFramesPerBurst;
2304  ma_proc AAudioStream_requestStart;
2305  ma_proc AAudioStream_requestStop;
2306  } aaudio;
2307 #endif
2308 #ifdef MA_SUPPORT_OPENSL
2309  struct
2310  {
2311  int _unused;
2312  } opensl;
2313 #endif
2314 #ifdef MA_SUPPORT_WEBAUDIO
2315  struct
2316  {
2317  int _unused;
2318  } webaudio;
2319 #endif
2320 #ifdef MA_SUPPORT_NULL
2321  struct
2322  {
2323  int _unused;
2325 #endif
2326  };
2327 
2328  union
2329  {
2330 #ifdef MA_WIN32
2331  struct
2332  {
2333  /*HMODULE*/ ma_handle hOle32DLL;
2334  ma_proc CoInitializeEx;
2335  ma_proc CoUninitialize;
2336  ma_proc CoCreateInstance;
2337  ma_proc CoTaskMemFree;
2338  ma_proc PropVariantClear;
2339  ma_proc StringFromGUID2;
2340 
2341  /*HMODULE*/ ma_handle hUser32DLL;
2342  ma_proc GetForegroundWindow;
2343  ma_proc GetDesktopWindow;
2344 
2345  /*HMODULE*/ ma_handle hAdvapi32DLL;
2346  ma_proc RegOpenKeyExA;
2347  ma_proc RegCloseKey;
2348  ma_proc RegQueryValueExA;
2349  } win32;
2350 #endif
2351 #ifdef MA_POSIX
2352  struct
2353  {
2371 #endif
2372  int _unused;
2373  };
2374 };
2375 
2377 {
2378  ma_context* pContext;
2380  ma_uint32 sampleRate;
2381  ma_uint32 state;
2382  ma_device_callback_proc onData;
2383  ma_stop_proc onStop;
2384  void* pUserData; /* Application defined data. */
2385  ma_mutex lock;
2386  ma_event wakeupEvent;
2387  ma_event startEvent;
2388  ma_event stopEvent;
2389  ma_thread thread;
2390  ma_result workResult; /* This is set by the worker thread after it's finished doing a job. */
2391  ma_bool32 usingDefaultSampleRate : 1;
2392  ma_bool32 usingDefaultBufferSize : 1;
2393  ma_bool32 usingDefaultPeriods : 1;
2394  ma_bool32 isOwnerOfContext : 1; /* When set to true, uninitializing the device will also uninitialize the context. Set to true when NULL is passed into ma_device_init(). */
2395  ma_bool32 noPreZeroedOutputBuffer : 1;
2396  ma_bool32 noClip : 1;
2397  float masterVolumeFactor;
2398  struct
2399  {
2400  char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */
2401  ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
2402  ma_bool32 usingDefaultFormat : 1;
2403  ma_bool32 usingDefaultChannels : 1;
2404  ma_bool32 usingDefaultChannelMap : 1;
2405  ma_format format;
2406  ma_uint32 channels;
2407  ma_channel channelMap[MA_MAX_CHANNELS];
2408  ma_format internalFormat;
2409  ma_uint32 internalChannels;
2410  ma_uint32 internalSampleRate;
2411  ma_channel internalChannelMap[MA_MAX_CHANNELS];
2412  ma_uint32 internalBufferSizeInFrames;
2413  ma_uint32 internalPeriods;
2414  ma_pcm_converter converter;
2415  ma_uint32 _dspFrameCount; /* Internal use only. Used as the data source when reading from the device. */
2416  const ma_uint8* _dspFrames; /* ^^^ AS ABOVE ^^^ */
2417  } playback;
2418  struct
2419  {
2420  char name[256]; /* Maybe temporary. Likely to be replaced with a query API. */
2421  ma_share_mode shareMode; /* Set to whatever was passed in when the device was initialized. */
2422  ma_bool32 usingDefaultFormat : 1;
2423  ma_bool32 usingDefaultChannels : 1;
2424  ma_bool32 usingDefaultChannelMap : 1;
2425  ma_format format;
2426  ma_uint32 channels;
2427  ma_channel channelMap[MA_MAX_CHANNELS];
2428  ma_format internalFormat;
2429  ma_uint32 internalChannels;
2430  ma_uint32 internalSampleRate;
2431  ma_channel internalChannelMap[MA_MAX_CHANNELS];
2432  ma_uint32 internalBufferSizeInFrames;
2433  ma_uint32 internalPeriods;
2434  ma_pcm_converter converter;
2435  ma_uint32 _dspFrameCount; /* Internal use only. Used as the data source when reading from the device. */
2436  const ma_uint8* _dspFrames; /* ^^^ AS ABOVE ^^^ */
2437  } capture;
2438 
2439  union
2440  {
2441 #ifdef MA_SUPPORT_WASAPI
2442  struct
2443  {
2444  /*IAudioClient**/ ma_ptr pAudioClientPlayback;
2445  /*IAudioClient**/ ma_ptr pAudioClientCapture;
2446  /*IAudioRenderClient**/ ma_ptr pRenderClient;
2447  /*IAudioCaptureClient**/ ma_ptr pCaptureClient;
2448  /*IMMDeviceEnumerator**/ ma_ptr pDeviceEnumerator; /* Used for IMMNotificationClient notifications. Required for detecting default device changes. */
2449  ma_IMMNotificationClient notificationClient;
2450  /*HANDLE*/ ma_handle hEventPlayback; /* Auto reset. Initialized to signaled. */
2451  /*HANDLE*/ ma_handle hEventCapture; /* Auto reset. Initialized to unsignaled. */
2452  ma_uint32 actualBufferSizeInFramesPlayback; /* Value from GetBufferSize(). internalBufferSizeInFrames is not set to the _actual_ buffer size when low-latency shared mode is being used due to the way the IAudioClient3 API works. */
2453  ma_uint32 actualBufferSizeInFramesCapture;
2454  ma_uint32 originalBufferSizeInFrames;
2455  ma_uint32 originalBufferSizeInMilliseconds;
2456  ma_uint32 originalPeriods;
2457  ma_bool32 hasDefaultPlaybackDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
2458  ma_bool32 hasDefaultCaptureDeviceChanged; /* <-- Make sure this is always a whole 32-bits because we use atomic assignments. */
2459  ma_uint32 periodSizeInFramesPlayback;
2460  ma_uint32 periodSizeInFramesCapture;
2461  ma_bool32 isStartedCapture;
2462  ma_bool32 isStartedPlayback;
2463  ma_bool32 noAutoConvertSRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM. */
2464  ma_bool32 noDefaultQualitySRC; /* When set to true, disables the use of AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY. */
2465  } wasapi;
2466 #endif
2467 #ifdef MA_SUPPORT_DSOUND
2468  struct
2469  {
2470  /*LPDIRECTSOUND*/ ma_ptr pPlayback;
2471  /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackPrimaryBuffer;
2472  /*LPDIRECTSOUNDBUFFER*/ ma_ptr pPlaybackBuffer;
2473  /*LPDIRECTSOUNDCAPTURE*/ ma_ptr pCapture;
2474  /*LPDIRECTSOUNDCAPTUREBUFFER*/ ma_ptr pCaptureBuffer;
2475  } dsound;
2476 #endif
2477 #ifdef MA_SUPPORT_WINMM
2478  struct
2479  {
2480  /*HWAVEOUT*/ ma_handle hDevicePlayback;
2481  /*HWAVEIN*/ ma_handle hDeviceCapture;
2482  /*HANDLE*/ ma_handle hEventPlayback;
2483  /*HANDLE*/ ma_handle hEventCapture;
2484  ma_uint32 fragmentSizeInFrames;
2485  ma_uint32 fragmentSizeInBytes;
2486  ma_uint32 iNextHeaderPlayback; /* [0,periods). Used as an index into pWAVEHDRPlayback. */
2487  ma_uint32 iNextHeaderCapture; /* [0,periods). Used as an index into pWAVEHDRCapture. */
2488  ma_uint32 headerFramesConsumedPlayback; /* The number of PCM frames consumed in the buffer in pWAVEHEADER[iNextHeader]. */
2489  ma_uint32 headerFramesConsumedCapture; /* ^^^ */
2490  /*WAVEHDR**/ ma_uint8* pWAVEHDRPlayback; /* One instantiation for each period. */
2491  /*WAVEHDR**/ ma_uint8* pWAVEHDRCapture; /* One instantiation for each period. */
2492  ma_uint8* pIntermediaryBufferPlayback;
2493  ma_uint8* pIntermediaryBufferCapture;
2494  ma_uint8* _pHeapData; /* Used internally and is used for the heap allocated data for the intermediary buffer and the WAVEHDR structures. */
2495  } winmm;
2496 #endif
2497 #ifdef MA_SUPPORT_ALSA
2498  struct
2499  {
2500  /*snd_pcm_t**/ ma_ptr pPCMPlayback;
2501  /*snd_pcm_t**/ ma_ptr pPCMCapture;
2502  ma_bool32 isUsingMMapPlayback : 1;
2503  ma_bool32 isUsingMMapCapture : 1;
2504  } alsa;
2505 #endif
2506 #ifdef MA_SUPPORT_PULSEAUDIO
2507  struct
2508  {
2509  /*pa_mainloop**/ ma_ptr pMainLoop;
2510  /*pa_mainloop_api**/ ma_ptr pAPI;
2511  /*pa_context**/ ma_ptr pPulseContext;
2512  /*pa_stream**/ ma_ptr pStreamPlayback;
2513  /*pa_stream**/ ma_ptr pStreamCapture;
2514  /*pa_context_state*/ ma_uint32 pulseContextState;
2515  void* pMappedBufferPlayback;
2516  const void* pMappedBufferCapture;
2517  ma_uint32 mappedBufferFramesRemainingPlayback;
2518  ma_uint32 mappedBufferFramesRemainingCapture;
2519  ma_uint32 mappedBufferFramesCapacityPlayback;
2520  ma_uint32 mappedBufferFramesCapacityCapture;
2521  ma_bool32 breakFromMainLoop : 1;
2522  } pulse;
2523 #endif
2524 #ifdef MA_SUPPORT_JACK
2525  struct
2526  {
2527  /*jack_client_t**/ ma_ptr pClient;
2528  /*jack_port_t**/ ma_ptr pPortsPlayback[MA_MAX_CHANNELS];
2529  /*jack_port_t**/ ma_ptr pPortsCapture[MA_MAX_CHANNELS];
2530  float* pIntermediaryBufferPlayback; /* Typed as a float because JACK is always floating point. */
2531  float* pIntermediaryBufferCapture;
2532  ma_pcm_rb duplexRB;
2533  } jack;
2534 #endif
2535 #ifdef MA_SUPPORT_COREAUDIO
2536  struct
2537  {
2538  ma_uint32 deviceObjectIDPlayback;
2539  ma_uint32 deviceObjectIDCapture;
2540  /*AudioUnit*/ ma_ptr audioUnitPlayback;
2541  /*AudioUnit*/ ma_ptr audioUnitCapture;
2542  /*AudioBufferList**/ ma_ptr pAudioBufferList; /* Only used for input devices. */
2543  ma_event stopEvent;
2544  ma_uint32 originalBufferSizeInFrames;
2545  ma_uint32 originalBufferSizeInMilliseconds;
2546  ma_uint32 originalPeriods;
2547  ma_bool32 isDefaultPlaybackDevice;
2548  ma_bool32 isDefaultCaptureDevice;
2549  ma_bool32 isSwitchingPlaybackDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
2550  ma_bool32 isSwitchingCaptureDevice; /* <-- Set to true when the default device has changed and miniaudio is in the process of switching. */
2551  ma_pcm_rb duplexRB;
2552  } coreaudio;
2553 #endif
2554 #ifdef MA_SUPPORT_SNDIO
2555  struct
2556  {
2557  ma_ptr handlePlayback;
2558  ma_ptr handleCapture;
2559  ma_bool32 isStartedPlayback;
2560  ma_bool32 isStartedCapture;
2561  } sndio;
2562 #endif
2563 #ifdef MA_SUPPORT_AUDIO4
2564  struct
2565  {
2566  int fdPlayback;
2567  int fdCapture;
2568  } audio4;
2569 #endif
2570 #ifdef MA_SUPPORT_OSS
2571  struct
2572  {
2573  int fdPlayback;
2574  int fdCapture;
2575  } oss;
2576 #endif
2577 #ifdef MA_SUPPORT_AAUDIO
2578  struct
2579  {
2580  /*AAudioStream**/ ma_ptr pStreamPlayback;
2581  /*AAudioStream**/ ma_ptr pStreamCapture;
2582  ma_pcm_rb duplexRB;
2583  } aaudio;
2584 #endif
2585 #ifdef MA_SUPPORT_OPENSL
2586  struct
2587  {
2588  /*SLObjectItf*/ ma_ptr pOutputMixObj;
2589  /*SLOutputMixItf*/ ma_ptr pOutputMix;
2590  /*SLObjectItf*/ ma_ptr pAudioPlayerObj;
2591  /*SLPlayItf*/ ma_ptr pAudioPlayer;
2592  /*SLObjectItf*/ ma_ptr pAudioRecorderObj;
2593  /*SLRecordItf*/ ma_ptr pAudioRecorder;
2594  /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueuePlayback;
2595  /*SLAndroidSimpleBufferQueueItf*/ ma_ptr pBufferQueueCapture;
2596  ma_uint32 currentBufferIndexPlayback;
2597  ma_uint32 currentBufferIndexCapture;
2598  ma_uint8* pBufferPlayback; /* This is malloc()'d and is used for storing audio data. Typed as ma_uint8 for easy offsetting. */
2599  ma_uint8* pBufferCapture;
2600  ma_pcm_rb duplexRB;
2601  } opensl;
2602 #endif
2603 #ifdef MA_SUPPORT_WEBAUDIO
2604  struct
2605  {
2606  int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */
2607  int indexCapture;
2608  ma_pcm_rb duplexRB; /* In external capture format. */
2609  } webaudio;
2610 #endif
2611 #ifdef MA_SUPPORT_NULL
2612  struct
2613  {
2614  ma_thread deviceThread;
2615  ma_event operationEvent;
2616  ma_event operationCompletionEvent;
2617  ma_uint32 operation;
2618  ma_result operationResult;
2619  ma_timer timer;
2620  double priorRunTime;
2621  ma_uint32 currentPeriodFramesRemainingPlayback;
2622  ma_uint32 currentPeriodFramesRemainingCapture;
2623  ma_uint64 lastProcessedFramePlayback;
2624  ma_uint32 lastProcessedFrameCapture;
2625  ma_bool32 isStarted;
2626  } null_device;
2627 #endif
2628  };
2629 };
2630 #if defined(_MSC_VER) && !defined(__clang__)
2631  #pragma warning(pop)
2632 #else
2633  #pragma GCC diagnostic pop /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */
2634 #endif
2635 
2636 /*
2637 Initializes a context.
2638 
2639 The context is used for selecting and initializing the relevant backends.
2640 
2641 Note that the location of the context cannot change throughout it's lifetime. Consider allocating
2642 the ma_context object with malloc() if this is an issue. The reason for this is that a pointer
2643 to the context is stored in the ma_device structure.
2644 
2645 <backends> is used to allow the application to prioritize backends depending on it's specific
2646 requirements. This can be null in which case it uses the default priority, which is as follows:
2647  - WASAPI
2648  - DirectSound
2649  - WinMM
2650  - Core Audio (Apple)
2651  - sndio
2652  - audio(4)
2653  - OSS
2654  - PulseAudio
2655  - ALSA
2656  - JACK
2657  - AAudio
2658  - OpenSL|ES
2659  - Web Audio / Emscripten
2660  - Null
2661 
2662 <pConfig> is used to configure the context. Use the logCallback config to set a callback for whenever a
2663 log message is posted. The priority of the worker thread can be set with the threadPriority config.
2664 
2665 It is recommended that only a single context is active at any given time because it's a bulky data
2666 structure which performs run-time linking for the relevant backends every time it's initialized.
2667 
2668 Return Value:
2669  MA_SUCCESS if successful; any other error code otherwise.
2670 
2671 Thread Safety: UNSAFE
2672 */
2673 ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext);
2674 
2675 /*
2676 Uninitializes a context.
2677 
2678 Results are undefined if you call this while any device created by this context is still active.
2679 
2680 Return Value:
2681  MA_SUCCESS if successful; any other error code otherwise.
2682 
2683 Thread Safety: UNSAFE
2684 */
2686 
2687 /*
2688 Enumerates over every device (both playback and capture).
2689 
2690 This is a lower-level enumeration function to the easier to use ma_context_get_devices(). Use
2691 ma_context_enumerate_devices() if you would rather not incur an internal heap allocation, or
2692 it simply suits your code better.
2693 
2694 Do _not_ assume the first enumerated device of a given type is the default device.
2695 
2696 Some backends and platforms may only support default playback and capture devices.
2697 
2698 Note that this only retrieves the ID and name/description of the device. The reason for only
2699 retrieving basic information is that it would otherwise require opening the backend device in
2700 order to probe it for more detailed information which can be inefficient. Consider using
2701 ma_context_get_device_info() for this, but don't call it from within the enumeration callback.
2702 
2703 In general, you should not do anything complicated from within the callback. In particular, do
2704 not try initializing a device from within the callback.
2705 
2706 Consider using ma_context_get_devices() for a simpler and safer API, albeit at the expense of
2707 an internal heap allocation.
2708 
2709 Returning false from the callback will stop enumeration. Returning true will continue enumeration.
2710 
2711 Return Value:
2712  MA_SUCCESS if successful; any other error code otherwise.
2713 
2714 Thread Safety: SAFE
2715  This is guarded using a simple mutex lock.
2716 */
2718 
2719 /*
2720 Retrieves basic information about every active playback and/or capture device.
2721 
2722 You can pass in NULL for the playback or capture lists in which case they'll be ignored.
2723 
2724 It is _not_ safe to assume the first device in the list is the default device.
2725 
2726 The returned pointers will become invalid upon the next call this this function, or when the
2727 context is uninitialized. Do not free the returned pointers.
2728 
2729 This function follows the same enumeration rules as ma_context_enumerate_devices(). See
2730 documentation for ma_context_enumerate_devices() for more information.
2731 
2732 Return Value:
2733  MA_SUCCESS if successful; any other error code otherwise.
2734 
2735 Thread Safety: SAFE
2736  Since each call to this function invalidates the pointers from the previous call, you
2737  should not be calling this simultaneously across multiple threads. Instead, you need to
2738  make a copy of the returned data with your own higher level synchronization.
2739 */
2740 ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount);
2741 
2742 /*
2743 Retrieves information about a device with the given ID.
2744 
2745 Do _not_ call this from within the ma_context_enumerate_devices() callback.
2746 
2747 It's possible for a device to have different information and capabilities depending on whether
2748 or not it's opened in shared or exclusive mode. For example, in shared mode, WASAPI always uses
2749 floating point samples for mixing, but in exclusive mode it can be anything. Therefore, this
2750 function allows you to specify which share mode you want information for. Note that not all
2751 backends and devices support shared or exclusive mode, in which case this function will fail
2752 if the requested share mode is unsupported.
2753 
2754 This leaves pDeviceInfo unmodified in the result of an error.
2755 
2756 Return Value:
2757  MA_SUCCESS if successful; any other error code otherwise.
2758 
2759 Thread Safety: SAFE
2760  This is guarded using a simple mutex lock.
2761 */
2762 ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo);
2763 
2764 /*
2765 Determines if the given context supports loopback mode.
2766 */
2768 
2769 /*
2770 Initializes a device.
2771 
2772 The context can be null in which case it uses the default. This is equivalent to passing in a
2773 context that was initialized like so:
2774 
2775  ma_context_init(NULL, 0, NULL, &context);
2776 
2777 Do not pass in null for the context if you are needing to open multiple devices. You can,
2778 however, use null when initializing the first device, and then use device.pContext for the
2779 initialization of other devices.
2780 
2781 The device's configuration is controlled with pConfig. This allows you to configure the sample
2782 format, channel count, sample rate, etc. Before calling ma_device_init(), you will need to
2783 initialize a ma_device_config object using ma_device_config_init(). You must set the callback in
2784 the device config. Once initialized, the device's config is immutable. If you need to change the
2785 config you will need to initialize a new device.
2786 
2787 Passing in 0 to any property in pConfig will force the use of a default value. In the case of
2788 sample format, channel count, sample rate and channel map it will default to the values used by
2789 the backend's internal device. For the size of the buffer you can set bufferSizeInFrames or
2790 bufferSizeInMilliseconds (if both are set it will prioritize bufferSizeInFrames). If both are
2791 set to zero, it will default to MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY or
2792 MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE, depending on whether or not performanceProfile
2793 is set to ma_performance_profile_low_latency or ma_performance_profile_conservative.
2794 
2795 If you request exclusive mode and the backend does not support it an error will be returned. For
2796 robustness, you may want to first try initializing the device in exclusive mode, and then fall back
2797 to shared mode if required. Alternatively you can just request shared mode (the default if you
2798 leave it unset in the config) which is the most reliable option. Some backends do not have a
2799 practical way of choosing whether or not the device should be exclusive or not (ALSA, for example)
2800 in which case it just acts as a hint. Unless you have special requirements you should try avoiding
2801 exclusive mode as it's intrusive to the user. Starting with Windows 10, miniaudio will use low-latency
2802 shared mode where possible which may make exclusive mode unnecessary.
2803 
2804 When sending or receiving data to/from a device, miniaudio will internally perform a format
2805 conversion to convert between the format specified by pConfig and the format used internally by
2806 the backend. If you pass in NULL for pConfig or 0 for the sample format, channel count,
2807 sample rate _and_ channel map, data transmission will run on an optimized pass-through fast path.
2808 
2809 The buffer size should be treated as a hint. miniaudio will try it's best to use exactly what you
2810 ask for, but it may differ. You should not assume the number of frames specified in each call to
2811 the data callback is exactly what you originally specified.
2812 
2813 The <periods> property controls how frequently the background thread is woken to check for more
2814 data. It's tied to the buffer size, so as an example, if your buffer size is equivalent to 10
2815 milliseconds and you have 2 periods, the CPU will wake up approximately every 5 milliseconds.
2816 
2817 When compiling for UWP you must ensure you call this function on the main UI thread because the
2818 operating system may need to present the user with a message asking for permissions. Please refer
2819 to the official documentation for ActivateAudioInterfaceAsync() for more information.
2820 
2821 ALSA Specific: When initializing the default device, requesting shared mode will try using the
2822 "dmix" device for playback and the "dsnoop" device for capture. If these fail it will try falling
2823 back to the "hw" device.
2824 
2825 Return Value:
2826  MA_SUCCESS if successful; any other error code otherwise.
2827 
2828 Thread Safety: UNSAFE
2829  It is not safe to call this function simultaneously for different devices because some backends
2830  depend on and mutate global state (such as OpenSL|ES). The same applies to calling this at the
2831  same time as ma_device_uninit().
2832 */
2833 ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice);
2834 
2835 /*
2836 Initializes a device without a context, with extra parameters for controlling the configuration
2837 of the internal self-managed context.
2838 
2839 See ma_device_init() and ma_context_init().
2840 */
2841 ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice);
2842 
2843 /*
2844 Uninitializes a device.
2845 
2846 This will explicitly stop the device. You do not need to call ma_device_stop() beforehand, but it's
2847 harmless if you do.
2848 
2849 Do not call this in any callback.
2850 
2851 Return Value:
2852  MA_SUCCESS if successful; any other error code otherwise.
2853 
2854 Thread Safety: UNSAFE
2855  As soon as this API is called the device should be considered undefined. All bets are off if you
2856  try using the device at the same time as uninitializing it.
2857 */
2859 
2860 /*
2861 Sets the callback to use when the device has stopped, either explicitly or as a result of an error.
2862 
2863 Thread Safety: SAFE
2864  This API is implemented as a simple atomic assignment.
2865 */
2867 
2868 /*
2869 Activates the device. For playback devices this begins playback. For capture devices it begins
2870 recording.
2871 
2872 For a playback device, this will retrieve an initial chunk of audio data from the client before
2873 returning. The reason for this is to ensure there is valid audio data in the buffer, which needs
2874 to be done _before_ the device begins playback.
2875 
2876 This API waits until the backend device has been started for real by the worker thread. It also
2877 waits on a mutex for thread-safety.
2878 
2879 Do not call this in any callback.
2880 
2881 Return Value:
2882  MA_SUCCESS if successful; any other error code otherwise.
2883 
2884 Thread Safety: SAFE
2885 */
2887 
2888 /*
2889 Puts the device to sleep, but does not uninitialize it. Use ma_device_start() to start it up again.
2890 
2891 This API needs to wait on the worker thread to stop the backend device properly before returning. It
2892 also waits on a mutex for thread-safety. In addition, some backends need to wait for the device to
2893 finish playback/recording of the current fragment which can take some time (usually proportionate to
2894 the buffer size that was specified at initialization time).
2895 
2896 This should not drop unprocessed samples. Backends are required to either pause the stream in-place
2897 or drain the buffer if pausing is not possible. The reason for this is that stopping the device and
2898 the resuming it with ma_device_start() (which you might do when your program loses focus) may result
2899 in a situation where those samples are never output to the speakers or received from the microphone
2900 which can in turn result in de-syncs.
2901 
2902 Do not call this in any callback.
2903 
2904 Return Value:
2905  MA_SUCCESS if successful; any other error code otherwise.
2906 
2907 Thread Safety: SAFE
2908 */
2910 
2911 /*
2912 Determines whether or not the device is started.
2913 
2914 This is implemented as a simple accessor.
2915 
2916 Return Value:
2917  True if the device is started, false otherwise.
2918 
2919 Thread Safety: SAFE
2920  If another thread calls ma_device_start() or ma_device_stop() at this same time as this function
2921  is called, there's a very small chance the return value will be out of sync.
2922 */
2924 
2925 /*
2926 Sets the master volume factor for the device.
2927 
2928 The volume factor must be between 0 (silence) and 1 (full volume). Use ma_device_set_master_gain_db() to
2929 use decibel notation, where 0 is full volume.
2930 
2931 This applies the volume factor across all channels.
2932 
2933 This does not change the operating system's volume. It only affects the volume for the given ma_device
2934 object's audio stream.
2935 
2936 Return Value
2937 ------------
2938 MA_SUCCESS if the volume was set successfully.
2939 MA_INVALID_ARGS if pDevice is NULL.
2940 MA_INVALID_ARGS if the volume factor is not within the range of [0, 1].
2941 */
2943 
2944 /*
2945 Retrieves the master volume factor for the device.
2946 
2947 Return Value
2948 ------------
2949 MA_SUCCESS if successful.
2950 MA_INVALID_ARGS if pDevice is NULL.
2951 MA_INVALID_ARGS if pVolume is NULL.
2952 */
2954 
2955 /*
2956 Sets the master volume for the device as gain in decibels.
2957 
2958 A gain of 0 is full volume, whereas a gain of < 0 will decrease the volume.
2959 
2960 This applies the gain across all channels.
2961 
2962 This does not change the operating system's volume. It only affects the volume for the given ma_device
2963 object's audio stream.
2964 
2965 Return Value
2966 ------------
2967 MA_SUCCESS if the volume was set successfully.
2968 MA_INVALID_ARGS if pDevice is NULL.
2969 MA_INVALID_ARGS if the gain is > 0.
2970 */
2972 
2973 /*
2974 Retrieves the master gain in decibels.
2975 
2976 Return Value
2977 ------------
2978 MA_SUCCESS if successful.
2979 MA_INVALID_ARGS if pDevice is NULL.
2980 MA_INVALID_ARGS if pGainDB is NULL.
2981 */
2983 
2984 
2985 /*
2986 Helper function for initializing a ma_context_config object.
2987 */
2989 
2990 /*
2991 Initializes a device config.
2992 
2993 By default, the device config will use native device settings (format, channels, sample rate, etc.). Using native
2994 settings means you will get an optimized pass-through data transmission pipeline to and from the device, but you will
2995 need to do all format conversions manually. Normally you would want to use a known format that your program can handle
2996 natively, which you can do by specifying it after this function returns, like so:
2997 
2998  ma_device_config config = ma_device_config_init(ma_device_type_playback);
2999  config.callback = my_data_callback;
3000  config.pUserData = pMyUserData;
3001  config.format = ma_format_f32;
3002  config.channels = 2;
3003  config.sampleRate = 44100;
3004 
3005 In this case miniaudio will perform all of the necessary data conversion for you behind the scenes.
3006 
3007 Currently miniaudio only supports asynchronous, callback based data delivery which means you must specify callback. A
3008 pointer to user data can also be specified which is set in the pUserData member of the ma_device object.
3009 
3010 To specify a channel map you can use ma_get_standard_channel_map():
3011 
3012  ma_get_standard_channel_map(ma_standard_channel_map_default, config.channels, config.channelMap);
3013 
3014 Alternatively you can set the channel map manually if you need something specific or something that isn't one of miniaudio's
3015 stock channel maps.
3016 
3017 By default the system's default device will be used. Set the pDeviceID member to a pointer to a ma_device_id object to
3018 use a specific device. You can enumerate over the devices with ma_context_enumerate_devices() or ma_context_get_devices()
3019 which will give you access to the device ID. Set pDeviceID to NULL to use the default device.
3020 
3021 The device type can be one of the ma_device_type's:
3022  ma_device_type_playback
3023  ma_device_type_capture
3024  ma_device_type_duplex
3025 
3026 Thread Safety: SAFE
3027 */
3029 
3030 
3031 /************************************************************************************************************************************************************
3032 
3033 Utiltities
3034 
3035 ************************************************************************************************************************************************************/
3036 
3037 /*
3038 Creates a mutex.
3039 
3040 A mutex must be created from a valid context. A mutex is initially unlocked.
3041 */
3043 
3044 /*
3045 Deletes a mutex.
3046 */
3048 
3049 /*
3050 Locks a mutex with an infinite timeout.
3051 */
3052 void ma_mutex_lock(ma_mutex* pMutex);
3053 
3054 /*
3055 Unlocks a mutex.
3056 */
3058 
3059 
3060 /*
3061 Retrieves a friendly name for a backend.
3062 */
3063 const char* ma_get_backend_name(ma_backend backend);
3064 
3065 /*
3066 Determines whether or not loopback mode is support by a backend.
3067 */
3069 
3070 
3071 /*
3072 Adjust buffer size based on a scaling factor.
3073 
3074 This just multiplies the base size by the scaling factor, making sure it's a size of at least 1.
3075 */
3076 ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale);
3077 
3078 /*
3079 Calculates a buffer size in milliseconds from the specified number of frames and sample rate.
3080 */
3082 
3083 /*
3084 Calculates a buffer size in frames from the specified number of milliseconds and sample rate.
3085 */
3087 
3088 /*
3089 Retrieves the default buffer size in milliseconds based on the specified performance profile.
3090 */
3092 
3093 /*
3094 Calculates a buffer size in frames for the specified performance profile and scale factor.
3095 */
3097 
3098 /*
3099 Copies silent frames into the given buffer.
3100 */
3101 void ma_zero_pcm_frames(void* p, ma_uint32 frameCount, ma_format format, ma_uint32 channels);
3102 
3103 /*
3104 Clips f32 samples.
3105 */
3106 void ma_clip_samples_f32(float* p, ma_uint32 sampleCount);
3107 MA_INLINE void ma_clip_pcm_frames_f32(float* p, ma_uint32 frameCount, ma_uint32 channels) { ma_clip_samples_f32(p, frameCount*channels); }
3108 
3109 /*
3110 Helper for applying a volume factor to samples.
3111 
3112 Note that the source and destination buffers can be the same, in which case it'll perform the operation in-place.
3113 */
3114 void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint32 sampleCount, float factor);
3115 void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint32 sampleCount, float factor);
3116 void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint32 sampleCount, float factor);
3117 void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint32 sampleCount, float factor);
3118 void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint32 sampleCount, float factor);
3119 
3120 void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint32 sampleCount, float factor);
3121 void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint32 sampleCount, float factor);
3122 void ma_apply_volume_factor_s24(void* pSamples, ma_uint32 sampleCount, float factor);
3123 void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint32 sampleCount, float factor);
3124 void ma_apply_volume_factor_f32(float* pSamples, ma_uint32 sampleCount, float factor);
3125 
3126 void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
3127 void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
3128 void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
3129 void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
3130 void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor);
3131 void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor);
3132 
3133 void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
3134 void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
3135 void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
3136 void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
3137 void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor);
3138 void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor);
3139 
3140 
3141 /*
3142 Helper for converting a linear factor to gain in decibels.
3143 */
3144 float ma_factor_to_gain_db(float factor);
3145 
3146 /*
3147 Helper for converting gain in decibels to a linear factor.
3148 */
3149 float ma_gain_db_to_factor(float gain);
3150 
3151 #endif /* MA_NO_DEVICE_IO */
3152 
3153 
3154 
3155 
3156 /************************************************************************************************************************************************************
3157 
3158 Decoding
3159 
3160 ************************************************************************************************************************************************************/
3161 #ifndef MA_NO_DECODING
3162 
3163 typedef struct ma_decoder ma_decoder;
3164 
3165 typedef enum
3166 {
3170 
3171 typedef size_t (* ma_decoder_read_proc) (ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead); /* Returns the number of bytes read. */
3172 typedef ma_bool32 (* ma_decoder_seek_proc) (ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin);
3176 
3177 typedef struct
3178 {
3179  ma_format format; /* Set to 0 or ma_format_unknown to use the stream's internal format. */
3180  ma_uint32 channels; /* Set to 0 to use the stream's internal channels. */
3181  ma_uint32 sampleRate; /* Set to 0 to use the stream's internal sample rate. */
3186  union
3187  {
3189  } src;
3191 
3193 {
3196  void* pUserData;
3197  ma_uint64 readPointer; /* Used for returning back to a previous position after analysing the stream or whatnot. */
3206  ma_pcm_converter dsp; /* <-- Format conversion is achieved by running frames through this. */
3210  void* pInternalDecoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */
3211  struct
3212  {
3213  const ma_uint8* pData;
3214  size_t dataSize;
3216  } memory; /* Only used for decoders that were opened against a block of memory. */
3217 };
3218 
3219 ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate);
3220 
3221 ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3222 ma_result ma_decoder_init_wav(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3223 ma_result ma_decoder_init_flac(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3225 ma_result ma_decoder_init_mp3(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3226 ma_result ma_decoder_init_raw(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder);
3227 
3228 ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3229 ma_result ma_decoder_init_memory_wav(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3230 ma_result ma_decoder_init_memory_flac(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3231 ma_result ma_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3232 ma_result ma_decoder_init_memory_mp3(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3233 ma_result ma_decoder_init_memory_raw(const void* pData, size_t dataSize, const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder);
3234 
3235 #ifndef MA_NO_STDIO
3236 ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3237 ma_result ma_decoder_init_file_wav(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3238 ma_result ma_decoder_init_file_flac(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3239 ma_result ma_decoder_init_file_vorbis(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3240 ma_result ma_decoder_init_file_mp3(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3241 
3242 ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3243 ma_result ma_decoder_init_file_wav_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3244 ma_result ma_decoder_init_file_flac_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3245 ma_result ma_decoder_init_file_vorbis_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3246 ma_result ma_decoder_init_file_mp3_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
3247 #endif
3248 
3250 
3251 /*
3252 Retrieves the length of the decoder in PCM frames.
3253 
3254 Do not call this on streams of an undefined length, such as internet radio.
3255 
3256 If the length is unknown or an error occurs, 0 will be returned.
3257 
3258 This will always return 0 for Vorbis decoders. This is due to a limitation with stb_vorbis in push mode which is what miniaudio
3259 uses internally.
3260 
3261 This will run in linear time for MP3 decoders. Do not call this in time critical scenarios.
3262 */
3264 
3265 ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount);
3267 
3268 /*
3269 Helper for opening and decoding a file into a heap allocated block of memory. Free the returned pointer with ma_free(). On input,
3270 pConfig should be set to what you want. On output it will be set to what you got.
3271 */
3272 #ifndef MA_NO_STDIO
3273 ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppDataOut);
3274 #endif
3275 ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppDataOut);
3276 
3277 #endif /* MA_NO_DECODING */
3278 
3279 
3280 /************************************************************************************************************************************************************
3281 
3282 Generation
3283 
3284 ************************************************************************************************************************************************************/
3285 typedef struct
3286 {
3287  double amplitude;
3288  double periodsPerSecond;
3289  double delta;
3290  double time;
3293 ma_result ma_sine_wave_init(double amplitude, double period, ma_uint32 sampleRate, ma_sine_wave* pSineWave);
3295 ma_uint64 ma_sine_wave_read_f32_ex(ma_sine_wave* pSineWave, ma_uint64 frameCount, ma_uint32 channels, ma_stream_layout layout, float** ppFrames);
3296 
3297 #ifdef __cplusplus
3298 }
3299 #endif
3300 #endif /* miniaudio_h */
3301 
3302 
3303 
3304 /************************************************************************************************************************************************************
3305 *************************************************************************************************************************************************************
3306 
3307 IMPLEMENTATION
3308 
3309 *************************************************************************************************************************************************************
3310 ************************************************************************************************************************************************************/
3311 #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
3312 #include <assert.h>
3313 #include <limits.h> /* For INT_MAX */
3314 #include <math.h> /* sin(), etc. */
3315 
3316 #if defined(MA_DEBUG_OUTPUT)
3317 #include <stdio.h> /* for printf() for debug output */
3318 #endif
3319 
3320 #ifdef MA_WIN32
3321 // @raysan5: To avoid conflicting windows.h symbols with raylib, so flags are defined
3322 // WARNING: Those flags avoid inclusion of some Win32 headers that could be required
3323 // by user at some point and won't be included...
3324 //-------------------------------------------------------------------------------------
3325 
3326 // If defined, the following flags inhibit definition of the indicated items.
3327 #define NOGDICAPMASKS // CC_*, LC_*, PC_*, CP_*, TC_*, RC_
3328 #define NOVIRTUALKEYCODES // VK_*
3329 #define NOWINMESSAGES // WM_*, EM_*, LB_*, CB_*
3330 #define NOWINSTYLES // WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
3331 #define NOSYSMETRICS // SM_*
3332 #define NOMENUS // MF_*
3333 #define NOICONS // IDI_*
3334 #define NOKEYSTATES // MK_*
3335 #define NOSYSCOMMANDS // SC_*
3336 #define NORASTEROPS // Binary and Tertiary raster ops
3337 #define NOSHOWWINDOW // SW_*
3338 #define OEMRESOURCE // OEM Resource values
3339 #define NOATOM // Atom Manager routines
3340 #define NOCLIPBOARD // Clipboard routines
3341 #define NOCOLOR // Screen colors
3342 #define NOCTLMGR // Control and Dialog routines
3343 #define NODRAWTEXT // DrawText() and DT_*
3344 #define NOGDI // All GDI defines and routines
3345 #define NOKERNEL // All KERNEL defines and routines
3346 #define NOUSER // All USER defines and routines
3347 //#define NONLS // All NLS defines and routines
3348 #define NOMB // MB_* and MessageBox()
3349 #define NOMEMMGR // GMEM_*, LMEM_*, GHND, LHND, associated routines
3350 #define NOMETAFILE // typedef METAFILEPICT
3351 #define NOMINMAX // Macros min(a,b) and max(a,b)
3352 #define NOMSG // typedef MSG and associated routines
3353 #define NOOPENFILE // OpenFile(), OemToAnsi, AnsiToOem, and OF_*
3354 #define NOSCROLL // SB_* and scrolling routines
3355 #define NOSERVICE // All Service Controller routines, SERVICE_ equates, etc.
3356 #define NOSOUND // Sound driver routines
3357 #define NOTEXTMETRIC // typedef TEXTMETRIC and associated routines
3358 #define NOWH // SetWindowsHook and WH_*
3359 #define NOWINOFFSETS // GWL_*, GCL_*, associated routines
3360 #define NOCOMM // COMM driver routines
3361 #define NOKANJI // Kanji support stuff.
3362 #define NOHELP // Help engine interface.
3363 #define NOPROFILER // Profiler interface.
3364 #define NODEFERWINDOWPOS // DeferWindowPos routines
3365 #define NOMCX // Modem Configuration Extensions
3366 
3367 // Type required before windows.h inclusion
3368 typedef struct tagMSG *LPMSG;
3369 
3370 #include <windows.h>
3371 
3372 // Type required by some unused function...
3373 typedef struct tagBITMAPINFOHEADER {
3374  DWORD biSize;
3375  LONG biWidth;
3376  LONG biHeight;
3377  WORD biPlanes;
3378  WORD biBitCount;
3379  DWORD biCompression;
3380  DWORD biSizeImage;
3381  LONG biXPelsPerMeter;
3382  LONG biYPelsPerMeter;
3383  DWORD biClrUsed;
3384  DWORD biClrImportant;
3385 } BITMAPINFOHEADER, *PBITMAPINFOHEADER;
3386 
3387 #include <objbase.h>
3388 #include <mmreg.h>
3389 #include <mmsystem.h>
3390 
3391 // @raysan5: Some required types defined for MSVC/TinyC compiler
3392 #if defined(_MSC_VER) || defined(__TINYC__)
3393  #include "propidl.h"
3394 #endif
3395 //----------------------------------------------------------------------------------
3396 #else
3397 #include <stdlib.h> /* For malloc(), free(), wcstombs(). */
3398 #include <string.h> /* For memset() */
3399 #endif
3400 
3401 #if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
3402 #include <mach/mach_time.h> /* For mach_absolute_time() */
3403 #endif
3404 
3405 #ifdef MA_POSIX
3406 #include <sys/time.h>
3407 #include <sys/types.h>
3408 #include <unistd.h>
3409 #include <dlfcn.h>
3410 #endif
3411 
3412 #ifdef MA_EMSCRIPTEN
3413 #include <emscripten/emscripten.h>
3414 #endif
3415 
3416 #if !defined(MA_64BIT) && !defined(MA_32BIT)
3417 #ifdef _WIN32
3418 #ifdef _WIN64
3419 #define MA_64BIT
3420 #else
3421 #define MA_32BIT
3422 #endif
3423 #endif
3424 #endif
3425 
3426 #if !defined(MA_64BIT) && !defined(MA_32BIT)
3427 #ifdef __GNUC__
3428 #ifdef __LP64__
3429 #define MA_64BIT
3430 #else
3431 #define MA_32BIT
3432 #endif
3433 #endif
3434 #endif
3435 
3436 #if !defined(MA_64BIT) && !defined(MA_32BIT)
3437 #include <stdint.h>
3438 #if INTPTR_MAX == INT64_MAX
3439 #define MA_64BIT
3440 #else
3441 #define MA_32BIT
3442 #endif
3443 #endif
3444 
3445 /* Architecture Detection */
3446 #if defined(__x86_64__) || defined(_M_X64)
3447 #define MA_X64
3448 #elif defined(__i386) || defined(_M_IX86)
3449 #define MA_X86
3450 #elif defined(__arm__) || defined(_M_ARM)
3451 #define MA_ARM
3452 #endif
3453 
3454 /* Cannot currently support AVX-512 if AVX is disabled. */
3455 #if !defined(MA_NO_AVX512) && defined(MA_NO_AVX2)
3456 #define MA_NO_AVX512
3457 #endif
3458 
3459 /* Intrinsics Support */
3460 #if defined(MA_X64) || defined(MA_X86)
3461  #if defined(_MSC_VER) && !defined(__clang__)
3462  /* MSVC. */
3463  #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */
3464  #define MA_SUPPORT_SSE2
3465  #endif
3466  /*#if _MSC_VER >= 1600 && !defined(MA_NO_AVX)*/ /* 2010 */
3467  /* #define MA_SUPPORT_AVX*/
3468  /*#endif*/
3469  #if _MSC_VER >= 1700 && !defined(MA_NO_AVX2) /* 2012 */
3470  #define MA_SUPPORT_AVX2
3471  #endif
3472  #if _MSC_VER >= 1910 && !defined(MA_NO_AVX512) /* 2017 */
3473  #define MA_SUPPORT_AVX512
3474  #endif
3475  #else
3476  /* Assume GNUC-style. */
3477  #if defined(__SSE2__) && !defined(MA_NO_SSE2)
3478  #define MA_SUPPORT_SSE2
3479  #endif
3480  /*#if defined(__AVX__) && !defined(MA_NO_AVX)*/
3481  /* #define MA_SUPPORT_AVX*/
3482  /*#endif*/
3483  #if defined(__AVX2__) && !defined(MA_NO_AVX2)
3484  #define MA_SUPPORT_AVX2
3485  #endif
3486  #if defined(__AVX512F__) && !defined(MA_NO_AVX512)
3487  #define MA_SUPPORT_AVX512
3488  #endif
3489  #endif
3490 
3491  /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */
3492  #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
3493  #if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include(<emmintrin.h>)
3494  #define MA_SUPPORT_SSE2
3495  #endif
3496  /*#if !defined(MA_SUPPORT_AVX) && !defined(MA_NO_AVX) && __has_include(<immintrin.h>)*/
3497  /* #define MA_SUPPORT_AVX*/
3498  /*#endif*/
3499  #if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include(<immintrin.h>)
3500  #define MA_SUPPORT_AVX2
3501  #endif
3502  #if !defined(MA_SUPPORT_AVX512) && !defined(MA_NO_AVX512) && __has_include(<zmmintrin.h>)
3503  #define MA_SUPPORT_AVX512
3504  #endif
3505  #endif
3506 
3507  #if defined(MA_SUPPORT_AVX512)
3508  #include <immintrin.h> /* Not a mistake. Intentionally including <immintrin.h> instead of <zmmintrin.h> because otherwise the compiler will complain. */
3509  #elif defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX)
3510  #include <immintrin.h>
3511  #elif defined(MA_SUPPORT_SSE2)
3512  #include <emmintrin.h>
3513  #endif
3514 #endif
3515 
3516 #if defined(MA_ARM)
3517  #if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
3518  #define MA_SUPPORT_NEON
3519  #endif
3520 
3521  /* Fall back to looking for the #include file. */
3522  #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
3523  #if !defined(MA_SUPPORT_NEON) && !defined(MA_NO_NEON) && __has_include(<arm_neon.h>)
3524  #define MA_SUPPORT_NEON
3525  #endif
3526  #endif
3527 
3528  #if defined(MA_SUPPORT_NEON)
3529  #include <arm_neon.h>
3530  #endif
3531 #endif
3532 
3533 #if defined(_MSC_VER)
3534  #pragma warning(push)
3535  #pragma warning(disable:4752) /* found Intel(R) Advanced Vector Extensions; consider using /arch:AVX */
3536 #endif
3537 
3538 #if defined(MA_X64) || defined(MA_X86)
3539  #if defined(_MSC_VER) && !defined(__clang__)
3540  #if _MSC_VER >= 1400
3541  #include <intrin.h>
3542  static MA_INLINE void ma_cpuid(int info[4], int fid)
3543  {
3544  __cpuid(info, fid);
3545  }
3546  #else
3547  #define MA_NO_CPUID
3548  #endif
3549 
3550  #if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219)
3551  static MA_INLINE unsigned __int64 ma_xgetbv(int reg)
3552  {
3553  return _xgetbv(reg);
3554  }
3555  #else
3556  #define MA_NO_XGETBV
3557  #endif
3558  #elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID)
3559  static MA_INLINE void ma_cpuid(int info[4], int fid)
3560  {
3561  /*
3562  It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the
3563  specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for
3564  supporting different assembly dialects.
3565 
3566  What's basically happening is that we're saving and restoring the ebx register manually.
3567  */
3568  #if defined(DRFLAC_X86) && defined(__PIC__)
3569  __asm__ __volatile__ (
3570  "xchg{l} {%%}ebx, %k1;"
3571  "cpuid;"
3572  "xchg{l} {%%}ebx, %k1;"
3573  : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
3574  );
3575  #else
3576  __asm__ __volatile__ (
3577  "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
3578  );
3579  #endif
3580  }
3581 
3582  static MA_INLINE ma_uint64 ma_xgetbv(int reg)
3583  {
3584  unsigned int hi;
3585  unsigned int lo;
3586 
3587  __asm__ __volatile__ (
3588  "xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg)
3589  );
3590 
3591  return ((ma_uint64)hi << 32) | (ma_uint64)lo;
3592  }
3593  #else
3594  #define MA_NO_CPUID
3595  #define MA_NO_XGETBV
3596  #endif
3597 #else
3598  #define MA_NO_CPUID
3599  #define MA_NO_XGETBV
3600 #endif
3601 
3602 static MA_INLINE ma_bool32 ma_has_sse2()
3603 {
3604 #if defined(MA_SUPPORT_SSE2)
3605  #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2)
3606  #if defined(MA_X64)
3607  return MA_TRUE; /* 64-bit targets always support SSE2. */
3608  #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
3609  return MA_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */
3610  #else
3611  #if defined(MA_NO_CPUID)
3612  return MA_FALSE;
3613  #else
3614  int info[4];
3615  ma_cpuid(info, 1);
3616  return (info[3] & (1 << 26)) != 0;
3617  #endif
3618  #endif
3619  #else
3620  return MA_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */
3621  #endif
3622 #else
3623  return MA_FALSE; /* No compiler support. */
3624 #endif
3625 }
3626 
3627 #if 0
3628 static MA_INLINE ma_bool32 ma_has_avx()
3629 {
3630 #if defined(MA_SUPPORT_AVX)
3631  #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX)
3632  #if defined(_AVX_) || defined(__AVX__)
3633  return MA_TRUE; /* If the compiler is allowed to freely generate AVX code we can assume support. */
3634  #else
3635  /* AVX requires both CPU and OS support. */
3636  #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
3637  return MA_FALSE;
3638  #else
3639  int info[4];
3640  ma_cpuid(info, 1);
3641  if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) {
3642  ma_uint64 xrc = ma_xgetbv(0);
3643  if ((xrc & 0x06) == 0x06) {
3644  return MA_TRUE;
3645  } else {
3646  return MA_FALSE;
3647  }
3648  } else {
3649  return MA_FALSE;
3650  }
3651  #endif
3652  #endif
3653  #else
3654  return MA_FALSE; /* AVX is only supported on x86 and x64 architectures. */
3655  #endif
3656 #else
3657  return MA_FALSE; /* No compiler support. */
3658 #endif
3659 }
3660 #endif
3661 
3662 static MA_INLINE ma_bool32 ma_has_avx2()
3663 {
3664 #if defined(MA_SUPPORT_AVX2)
3665  #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2)
3666  #if defined(_AVX2_) || defined(__AVX2__)
3667  return MA_TRUE; /* If the compiler is allowed to freely generate AVX2 code we can assume support. */
3668  #else
3669  /* AVX2 requires both CPU and OS support. */
3670  #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
3671  return MA_FALSE;
3672  #else
3673  int info1[4];
3674  int info7[4];
3675  ma_cpuid(info1, 1);
3676  ma_cpuid(info7, 7);
3677  if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) {
3678  ma_uint64 xrc = ma_xgetbv(0);
3679  if ((xrc & 0x06) == 0x06) {
3680  return MA_TRUE;
3681  } else {
3682  return MA_FALSE;
3683  }
3684  } else {
3685  return MA_FALSE;
3686  }
3687  #endif
3688  #endif
3689  #else
3690  return MA_FALSE; /* AVX2 is only supported on x86 and x64 architectures. */
3691  #endif
3692 #else
3693  return MA_FALSE; /* No compiler support. */
3694 #endif
3695 }
3696 
3697 static MA_INLINE ma_bool32 ma_has_avx512f()
3698 {
3699 #if defined(MA_SUPPORT_AVX512)
3700  #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX512)
3701  #if defined(__AVX512F__)
3702  return MA_TRUE; /* If the compiler is allowed to freely generate AVX-512F code we can assume support. */
3703  #else
3704  /* AVX-512 requires both CPU and OS support. */
3705  #if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
3706  return MA_FALSE;
3707  #else
3708  int info1[4];
3709  int info7[4];
3710  ma_cpuid(info1, 1);
3711  ma_cpuid(info7, 7);
3712  if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 16)) != 0)) {
3713  ma_uint64 xrc = ma_xgetbv(0);
3714  if ((xrc & 0xE6) == 0xE6) {
3715  return MA_TRUE;
3716  } else {
3717  return MA_FALSE;
3718  }
3719  } else {
3720  return MA_FALSE;
3721  }
3722  #endif
3723  #endif
3724  #else
3725  return MA_FALSE; /* AVX-512F is only supported on x86 and x64 architectures. */
3726  #endif
3727 #else
3728  return MA_FALSE; /* No compiler support. */
3729 #endif
3730 }
3731 
3732 static MA_INLINE ma_bool32 ma_has_neon()
3733 {
3734 #if defined(MA_SUPPORT_NEON)
3735  #if defined(MA_ARM) && !defined(MA_NO_NEON)
3736  #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
3737  return MA_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */
3738  #else
3739  /* TODO: Runtime check. */
3740  return MA_FALSE;
3741  #endif
3742  #else
3743  return MA_FALSE; /* NEON is only supported on ARM architectures. */
3744  #endif
3745 #else
3746  return MA_FALSE; /* No compiler support. */
3747 #endif
3748 }
3749 
3750 
3751 static MA_INLINE ma_bool32 ma_is_little_endian()
3752 {
3753 #if defined(MA_X86) || defined(MA_X64)
3754  return MA_TRUE;
3755 #else
3756  int n = 1;
3757  return (*(char*)&n) == 1;
3758 #endif
3759 }
3760 
3761 static MA_INLINE ma_bool32 ma_is_big_endian()
3762 {
3763  return !ma_is_little_endian();
3764 }
3765 
3766 
3767 #ifndef MA_COINIT_VALUE
3768 #define MA_COINIT_VALUE 0 /* 0 = COINIT_MULTITHREADED*/
3769 #endif
3770 
3771 
3772 
3773 #ifndef MA_PI
3774 #define MA_PI 3.14159265358979323846264f
3775 #endif
3776 #ifndef MA_PI_D
3777 #define MA_PI_D 3.14159265358979323846264
3778 #endif
3779 #ifndef MA_TAU
3780 #define MA_TAU 6.28318530717958647693f
3781 #endif
3782 #ifndef MA_TAU_D
3783 #define MA_TAU_D 6.28318530717958647693
3784 #endif
3785 
3786 
3787 /* The default format when ma_format_unknown (0) is requested when initializing a device. */
3788 #ifndef MA_DEFAULT_FORMAT
3789 #define MA_DEFAULT_FORMAT ma_format_f32
3790 #endif
3791 
3792 /* The default channel count to use when 0 is used when initializing a device. */
3793 #ifndef MA_DEFAULT_CHANNELS
3794 #define MA_DEFAULT_CHANNELS 2
3795 #endif
3796 
3797 /* The default sample rate to use when 0 is used when initializing a device. */
3798 #ifndef MA_DEFAULT_SAMPLE_RATE
3799 #define MA_DEFAULT_SAMPLE_RATE 48000
3800 #endif
3801 
3802 /* Default periods when none is specified in ma_device_init(). More periods means more work on the CPU. */
3803 #ifndef MA_DEFAULT_PERIODS
3804 #define MA_DEFAULT_PERIODS 3
3805 #endif
3806 
3807 /* The base buffer size in milliseconds for low latency mode. */
3808 #ifndef MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY
3809 #define MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY (10*MA_DEFAULT_PERIODS)
3810 #endif
3811 
3812 /* The base buffer size in milliseconds for conservative mode. */
3813 #ifndef MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE
3814 #define MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE (100*MA_DEFAULT_PERIODS)
3815 #endif
3816 
3817 
3818 /* Standard sample rates, in order of priority. */
3819 ma_uint32 g_maStandardSampleRatePriorities[] = {
3820  MA_SAMPLE_RATE_48000, /* Most common */
3822 
3823  MA_SAMPLE_RATE_32000, /* Lows */
3826 
3827  MA_SAMPLE_RATE_88200, /* Highs */
3831 
3832  MA_SAMPLE_RATE_16000, /* Extreme lows */
3835 
3836  MA_SAMPLE_RATE_352800, /* Extreme highs */
3838 };
3839 
3840 ma_format g_maFormatPriorities[] = {
3841  ma_format_s16, /* Most common */
3842  ma_format_f32,
3843 
3844  /*ma_format_s24_32,*/ /* Clean alignment */
3845  ma_format_s32,
3846 
3847  ma_format_s24, /* Unclean alignment */
3848 
3849  ma_format_u8 /* Low quality */
3850 };
3851 
3852 
3853 
3854 /******************************************************************************
3855 
3856 Standard Library Stuff
3857 
3858 ******************************************************************************/
3859 #ifndef MA_MALLOC
3860 #ifdef MA_WIN32
3861 #define MA_MALLOC(sz) HeapAlloc(GetProcessHeap(), 0, (sz))
3862 #else
3863 #define MA_MALLOC(sz) malloc((sz))
3864 #endif
3865 #endif
3866 
3867 #ifndef MA_REALLOC
3868 #ifdef MA_WIN32
3869 #define MA_REALLOC(p, sz) (((sz) > 0) ? ((p) ? HeapReAlloc(GetProcessHeap(), 0, (p), (sz)) : HeapAlloc(GetProcessHeap(), 0, (sz))) : ((VOID*)(size_t)(HeapFree(GetProcessHeap(), 0, (p)) & 0)))
3870 #else
3871 #define MA_REALLOC(p, sz) realloc((p), (sz))
3872 #endif
3873 #endif
3874 
3875 #ifndef MA_FREE
3876 #ifdef MA_WIN32
3877 #define MA_FREE(p) HeapFree(GetProcessHeap(), 0, (p))
3878 #else
3879 #define MA_FREE(p) free((p))
3880 #endif
3881 #endif
3882 
3883 #ifndef MA_ZERO_MEMORY
3884 #ifdef MA_WIN32
3885 #define MA_ZERO_MEMORY(p, sz) ZeroMemory((p), (sz))
3886 #else
3887 #define MA_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
3888 #endif
3889 #endif
3890 
3891 #ifndef MA_COPY_MEMORY
3892 #ifdef MA_WIN32
3893 #define MA_COPY_MEMORY(dst, src, sz) CopyMemory((dst), (src), (sz))
3894 #else
3895 #define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
3896 #endif
3897 #endif
3898 
3899 #ifndef MA_ASSERT
3900 #ifdef MA_WIN32
3901 #define MA_ASSERT(condition) assert(condition)
3902 #else
3903 #define MA_ASSERT(condition) assert(condition)
3904 #endif
3905 #endif
3906 
3907 #define ma_zero_memory MA_ZERO_MEMORY
3908 #define ma_copy_memory MA_COPY_MEMORY
3909 #define ma_assert MA_ASSERT
3910 
3911 #define ma_zero_object(p) ma_zero_memory((p), sizeof(*(p)))
3912 #define ma_countof(x) (sizeof(x) / sizeof(x[0]))
3913 #define ma_max(x, y) (((x) > (y)) ? (x) : (y))
3914 #define ma_min(x, y) (((x) < (y)) ? (x) : (y))
3915 #define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi)))
3916 #define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset))
3917 
3918 #define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels))
3919 
3920 /*
3921 Return Values:
3922  0: Success
3923  22: EINVAL
3924  34: ERANGE
3925 
3926 Not using symbolic constants for errors because I want to avoid #including errno.h
3927 */
3928 int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
3929 {
3930  size_t i;
3931 
3932  if (dst == 0) {
3933  return 22;
3934  }
3935  if (dstSizeInBytes == 0) {
3936  return 34;
3937  }
3938  if (src == 0) {
3939  dst[0] = '\0';
3940  return 22;
3941  }
3942 
3943  for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
3944  dst[i] = src[i];
3945  }
3946 
3947  if (i < dstSizeInBytes) {
3948  dst[i] = '\0';
3949  return 0;
3950  }
3951 
3952  dst[0] = '\0';
3953  return 34;
3954 }
3955 
3956 int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
3957 {
3958  size_t maxcount;
3959  size_t i;
3960 
3961  if (dst == 0) {
3962  return 22;
3963  }
3964  if (dstSizeInBytes == 0) {
3965  return 34;
3966  }
3967  if (src == 0) {
3968  dst[0] = '\0';
3969  return 22;
3970  }
3971 
3972  maxcount = count;
3973  if (count == ((size_t)-1) || count >= dstSizeInBytes) { /* -1 = _TRUNCATE */
3974  maxcount = dstSizeInBytes - 1;
3975  }
3976 
3977  for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
3978  dst[i] = src[i];
3979  }
3980 
3981  if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
3982  dst[i] = '\0';
3983  return 0;
3984  }
3985 
3986  dst[0] = '\0';
3987  return 34;
3988 }
3989 
3990 int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
3991 {
3992  char* dstorig;
3993 
3994  if (dst == 0) {
3995  return 22;
3996  }
3997  if (dstSizeInBytes == 0) {
3998  return 34;
3999  }
4000  if (src == 0) {
4001  dst[0] = '\0';
4002  return 22;
4003  }
4004 
4005  dstorig = dst;
4006 
4007  while (dstSizeInBytes > 0 && dst[0] != '\0') {
4008  dst += 1;
4009  dstSizeInBytes -= 1;
4010  }
4011 
4012  if (dstSizeInBytes == 0) {
4013  return 22; /* Unterminated. */
4014  }
4015 
4016 
4017  while (dstSizeInBytes > 0 && src[0] != '\0') {
4018  *dst++ = *src++;
4019  dstSizeInBytes -= 1;
4020  }
4021 
4022  if (dstSizeInBytes > 0) {
4023  dst[0] = '\0';
4024  } else {
4025  dstorig[0] = '\0';
4026  return 34;
4027  }
4028 
4029  return 0;
4030 }
4031 
4032 int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
4033 {
4034  char* dstorig;
4035 
4036  if (dst == 0) {
4037  return 22;
4038  }
4039  if (dstSizeInBytes == 0) {
4040  return 34;
4041  }
4042  if (src == 0) {
4043  return 22;
4044  }
4045 
4046  dstorig = dst;
4047 
4048  while (dstSizeInBytes > 0 && dst[0] != '\0') {
4049  dst += 1;
4050  dstSizeInBytes -= 1;
4051  }
4052 
4053  if (dstSizeInBytes == 0) {
4054  return 22; /* Unterminated. */
4055  }
4056 
4057 
4058  if (count == ((size_t)-1)) { /* _TRUNCATE */
4059  count = dstSizeInBytes - 1;
4060  }
4061 
4062  while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) {
4063  *dst++ = *src++;
4064  dstSizeInBytes -= 1;
4065  count -= 1;
4066  }
4067 
4068  if (dstSizeInBytes > 0) {
4069  dst[0] = '\0';
4070  } else {
4071  dstorig[0] = '\0';
4072  return 34;
4073  }
4074 
4075  return 0;
4076 }
4077 
4078 int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix)
4079 {
4080  int sign;
4081  unsigned int valueU;
4082  char* dstEnd;
4083 
4084  if (dst == NULL || dstSizeInBytes == 0) {
4085  return 22;
4086  }
4087  if (radix < 2 || radix > 36) {
4088  dst[0] = '\0';
4089  return 22;
4090  }
4091 
4092  sign = (value < 0 && radix == 10) ? -1 : 1; /* The negative sign is only used when the base is 10. */
4093 
4094  if (value < 0) {
4095  valueU = -value;
4096  } else {
4097  valueU = value;
4098  }
4099 
4100  dstEnd = dst;
4101  do
4102  {
4103  int remainder = valueU % radix;
4104  if (remainder > 9) {
4105  *dstEnd = (char)((remainder - 10) + 'a');
4106  } else {
4107  *dstEnd = (char)(remainder + '0');
4108  }
4109 
4110  dstEnd += 1;
4111  dstSizeInBytes -= 1;
4112  valueU /= radix;
4113  } while (dstSizeInBytes > 0 && valueU > 0);
4114 
4115  if (dstSizeInBytes == 0) {
4116  dst[0] = '\0';
4117  return 22; /* Ran out of room in the output buffer. */
4118  }
4119 
4120  if (sign < 0) {
4121  *dstEnd++ = '-';
4122  dstSizeInBytes -= 1;
4123  }
4124 
4125  if (dstSizeInBytes == 0) {
4126  dst[0] = '\0';
4127  return 22; /* Ran out of room in the output buffer. */
4128  }
4129 
4130  *dstEnd = '\0';
4131 
4132 
4133  /* At this point the string will be reversed. */
4134  dstEnd -= 1;
4135  while (dst < dstEnd) {
4136  char temp = *dst;
4137  *dst = *dstEnd;
4138  *dstEnd = temp;
4139 
4140  dst += 1;
4141  dstEnd -= 1;
4142  }
4143 
4144  return 0;
4145 }
4146 
4147 int ma_strcmp(const char* str1, const char* str2)
4148 {
4149  if (str1 == str2) return 0;
4150 
4151  /* These checks differ from the standard implementation. It's not important, but I prefer it just for sanity. */
4152  if (str1 == NULL) return -1;
4153  if (str2 == NULL) return 1;
4154 
4155  for (;;) {
4156  if (str1[0] == '\0') {
4157  break;
4158  }
4159  if (str1[0] != str2[0]) {
4160  break;
4161  }
4162 
4163  str1 += 1;
4164  str2 += 1;
4165  }
4166 
4167  return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0];
4168 }
4169 
4170 int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB)
4171 {
4172  int result;
4173 
4174  result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1);
4175  if (result != 0) {
4176  return result;
4177  }
4178 
4179  result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1);
4180  if (result != 0) {
4181  return result;
4182  }
4183 
4184  return result;
4185 }
4186 
4187 char* ma_copy_string(const char* src)
4188 {
4189  size_t sz = strlen(src)+1;
4190  char* dst = (char*)ma_malloc(sz);
4191  if (dst == NULL) {
4192  return NULL;
4193  }
4194 
4195  ma_strcpy_s(dst, sz, src);
4196 
4197  return dst;
4198 }
4199 
4200 
4201 /* Thanks to good old Bit Twiddling Hacks for this one: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 */
4202 static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x)
4203 {
4204  x--;
4205  x |= x >> 1;
4206  x |= x >> 2;
4207  x |= x >> 4;
4208  x |= x >> 8;
4209  x |= x >> 16;
4210  x++;
4211 
4212  return x;
4213 }
4214 
4215 static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x)
4216 {
4217  return ma_next_power_of_2(x) >> 1;
4218 }
4219 
4220 static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x)
4221 {
4222  unsigned int prev = ma_prev_power_of_2(x);
4223  unsigned int next = ma_next_power_of_2(x);
4224  if ((next - x) > (x - prev)) {
4225  return prev;
4226  } else {
4227  return next;
4228  }
4229 }
4230 
4231 static MA_INLINE unsigned int ma_count_set_bits(unsigned int x)
4232 {
4233  unsigned int count = 0;
4234  while (x != 0) {
4235  if (x & 1) {
4236  count += 1;
4237  }
4238 
4239  x = x >> 1;
4240  }
4241 
4242  return count;
4243 }
4244 
4245 
4246 
4247 /* Clamps an f32 sample to -1..1 */
4248 static MA_INLINE float ma_clip_f32(float x)
4249 {
4250  if (x < -1) return -1;
4251  if (x > +1) return +1;
4252  return x;
4253 }
4254 
4255 static MA_INLINE float ma_mix_f32(float x, float y, float a)
4256 {
4257  return x*(1-a) + y*a;
4258 }
4259 static MA_INLINE float ma_mix_f32_fast(float x, float y, float a)
4260 {
4261  float r0 = (y - x);
4262  float r1 = r0*a;
4263  return x + r1;
4264  /*return x + (y - x)*a;*/
4265 }
4266 
4267 #if defined(MA_SUPPORT_SSE2)
4268 static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a)
4269 {
4270  return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a));
4271 }
4272 #endif
4273 #if defined(MA_SUPPORT_AVX2)
4274 static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a)
4275 {
4276  return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a));
4277 }
4278 #endif
4279 #if defined(MA_SUPPORT_AVX512)
4280 static MA_INLINE __m512 ma_mix_f32_fast__avx512(__m512 x, __m512 y, __m512 a)
4281 {
4282  return _mm512_add_ps(x, _mm512_mul_ps(_mm512_sub_ps(y, x), a));
4283 }
4284 #endif
4285 #if defined(MA_SUPPORT_NEON)
4286 static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a)
4287 {
4288  return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a));
4289 }
4290 #endif
4291 
4292 
4293 static MA_INLINE double ma_mix_f64(double x, double y, double a)
4294 {
4295  return x*(1-a) + y*a;
4296 }
4297 static MA_INLINE double ma_mix_f64_fast(double x, double y, double a)
4298 {
4299  return x + (y - x)*a;
4300 }
4301 
4302 static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi)
4303 {
4304  return lo + x*(hi-lo);
4305 }
4306 
4307 
4308 /*
4309 Random Number Generation
4310 
4311 miniaudio uses the LCG random number generation algorithm. This is good enough for audio.
4312 
4313 Note that miniaudio's LCG implementation uses global state which is _not_ thread-local. When this is called across
4314 multiple threads, results will be unpredictable. However, it won't crash and results will still be random enough
4315 for miniaudio's purposes.
4316 */
4317 #define MA_LCG_M 2147483647
4318 #define MA_LCG_A 48271
4319 #define MA_LCG_C 0
4320 static ma_int32 g_maLCG = 4321; /* Non-zero initial seed. Use ma_seed() to use an explicit seed. */
4321 
4322 void ma_seed(ma_int32 seed)
4323 {
4324  g_maLCG = seed;
4325 }
4326 
4327 ma_int32 ma_rand_s32()
4328 {
4329  ma_int32 lcg = g_maLCG;
4330  ma_int32 r = (MA_LCG_A * lcg + MA_LCG_C) % MA_LCG_M;
4331  g_maLCG = r;
4332  return r;
4333 }
4334 
4335 ma_uint32 ma_rand_u32()
4336 {
4337  return (ma_uint32)ma_rand_s32();
4338 }
4339 
4340 double ma_rand_f64()
4341 {
4342  return ma_rand_s32() / (double)0x7FFFFFFF;
4343 }
4344 
4345 float ma_rand_f32()
4346 {
4347  return (float)ma_rand_f64();
4348 }
4349 
4350 float ma_rand_range_f32(float lo, float hi)
4351 {
4352  return ma_scale_to_range_f32(ma_rand_f32(), lo, hi);
4353 }
4354 
4355 ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi)
4356 {
4357  if (lo == hi) {
4358  return lo;
4359  }
4360 
4361  return lo + ma_rand_u32() / (0xFFFFFFFF / (hi - lo + 1) + 1);
4362 }
4363 
4364 
4365 static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax)
4366 {
4367  return ma_rand_range_f32(ditherMin, ditherMax);
4368 }
4369 
4370 static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax)
4371 {
4372  float a = ma_rand_range_f32(ditherMin, 0);
4373  float b = ma_rand_range_f32(0, ditherMax);
4374  return a + b;
4375 }
4376 
4377 static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax)
4378 {
4379  if (ditherMode == ma_dither_mode_rectangle) {
4380  return ma_dither_f32_rectangle(ditherMin, ditherMax);
4381  }
4382  if (ditherMode == ma_dither_mode_triangle) {
4383  return ma_dither_f32_triangle(ditherMin, ditherMax);
4384  }
4385 
4386  return 0;
4387 }
4388 
4389 static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax)
4390 {
4391  if (ditherMode == ma_dither_mode_rectangle) {
4392  ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax);
4393  return a;
4394  }
4395  if (ditherMode == ma_dither_mode_triangle) {
4396  ma_int32 a = ma_rand_range_s32(ditherMin, 0);
4397  ma_int32 b = ma_rand_range_s32(0, ditherMax);
4398  return a + b;
4399  }
4400 
4401  return 0;
4402 }
4403 
4404 
4405 /*
4406 Splits a buffer into parts of equal length and of the given alignment. The returned size of the split buffers will be a
4407 multiple of the alignment. The alignment must be a power of 2.
4408 */
4409 void ma_split_buffer(void* pBuffer, size_t bufferSize, size_t splitCount, size_t alignment, void** ppBuffersOut, size_t* pSplitSizeOut)
4410 {
4411  ma_uintptr pBufferUnaligned;
4412  ma_uintptr pBufferAligned;
4413  size_t unalignedBytes;
4414  size_t splitSize;
4415 
4416  if (pSplitSizeOut) {
4417  *pSplitSizeOut = 0;
4418  }
4419 
4420  if (pBuffer == NULL || bufferSize == 0 || splitCount == 0) {
4421  return;
4422  }
4423 
4424  if (alignment == 0) {
4425  alignment = 1;
4426  }
4427 
4428  pBufferUnaligned = (ma_uintptr)pBuffer;
4429  pBufferAligned = (pBufferUnaligned + (alignment-1)) & ~(alignment-1);
4430  unalignedBytes = (size_t)(pBufferAligned - pBufferUnaligned);
4431 
4432  splitSize = 0;
4433  if (bufferSize >= unalignedBytes) {
4434  splitSize = (bufferSize - unalignedBytes) / splitCount;
4435  splitSize = splitSize & ~(alignment-1);
4436  }
4437 
4438  if (ppBuffersOut != NULL) {
4439  size_t i;
4440  for (i = 0; i < splitCount; ++i) {
4441  ppBuffersOut[i] = (ma_uint8*)(pBufferAligned + (splitSize*i));
4442  }
4443  }
4444 
4445  if (pSplitSizeOut) {
4446  *pSplitSizeOut = splitSize;
4447  }
4448 }
4449 
4450 
4451 /******************************************************************************
4452 
4453 Atomics
4454 
4455 ******************************************************************************/
4456 #if defined(__clang__)
4457  #if defined(__has_builtin)
4458  #if __has_builtin(__sync_swap)
4459  #define MA_HAS_SYNC_SWAP
4460  #endif
4461  #endif
4462 #elif defined(__GNUC__)
4463  #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC__ >= 7)
4464  #define MA_HAS_GNUC_ATOMICS
4465  #endif
4466 #endif
4467 
4468 #if defined(_WIN32) && !defined(__GNUC__) && !defined(__clang__)
4469 #define ma_memory_barrier() MemoryBarrier()
4470 #define ma_atomic_exchange_32(a, b) InterlockedExchange((LONG*)a, (LONG)b)
4471 #define ma_atomic_exchange_64(a, b) InterlockedExchange64((LONGLONG*)a, (LONGLONG)b)
4472 #define ma_atomic_increment_32(a) InterlockedIncrement((LONG*)a)
4473 #define ma_atomic_decrement_32(a) InterlockedDecrement((LONG*)a)
4474 #else
4475 #define ma_memory_barrier() __sync_synchronize()
4476 #if defined(MA_HAS_SYNC_SWAP)
4477  #define ma_atomic_exchange_32(a, b) __sync_swap(a, b)
4478  #define ma_atomic_exchange_64(a, b) __sync_swap(a, b)
4479 #elif defined(MA_HAS_GNUC_ATOMICS)
4480  #define ma_atomic_exchange_32(a, b) (void)__atomic_exchange_n(a, b, __ATOMIC_ACQ_REL)
4481  #define ma_atomic_exchange_64(a, b) (void)__atomic_exchange_n(a, b, __ATOMIC_ACQ_REL)
4482 #else
4483  #define ma_atomic_exchange_32(a, b) __sync_synchronize(); (void)__sync_lock_test_and_set(a, b)
4484  #define ma_atomic_exchange_64(a, b) __sync_synchronize(); (void)__sync_lock_test_and_set(a, b)
4485 #endif
4486 #define ma_atomic_increment_32(a) __sync_add_and_fetch(a, 1)
4487 #define ma_atomic_decrement_32(a) __sync_sub_and_fetch(a, 1)
4488 #endif
4489 
4490 #ifdef MA_64BIT
4491 #define ma_atomic_exchange_ptr ma_atomic_exchange_64
4492 #endif
4493 #ifdef MA_32BIT
4494 #define ma_atomic_exchange_ptr ma_atomic_exchange_32
4495 #endif
4496 
4497 
4498 ma_uint32 ma_get_standard_sample_rate_priority_index(ma_uint32 sampleRate) /* Lower = higher priority */
4499 {
4500  ma_uint32 i;
4501  for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) {
4502  if (g_maStandardSampleRatePriorities[i] == sampleRate) {
4503  return i;
4504  }
4505  }
4506 
4507  return (ma_uint32)-1;
4508 }
4509 
4510 ma_uint64 ma_calculate_frame_count_after_src(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn)
4511 {
4512  double srcRatio = (double)sampleRateOut / sampleRateIn;
4513  double frameCountOutF = (ma_int64)frameCountIn * srcRatio; /* Cast to int64 required for VC6. */
4514  ma_uint64 frameCountOut = (ma_uint64)frameCountOutF;
4515 
4516  /* If the output frame count is fractional, make sure we add an extra frame to ensure there's enough room for that last sample. */
4517  if ((frameCountOutF - (ma_int64)frameCountOut) > 0.0) {
4518  frameCountOut += 1;
4519  }
4520 
4521  return frameCountOut;
4522 }
4523 
4524 
4525 /************************************************************************************************************************************************************
4526 *************************************************************************************************************************************************************
4527 
4528 DEVICE I/O
4529 ==========
4530 
4531 *************************************************************************************************************************************************************
4532 ************************************************************************************************************************************************************/
4533 #ifndef MA_NO_DEVICE_IO
4534 /*
4535 Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When
4536 using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing
4537 compile-time linking fixes this. I'm not sure why this happens, but the safest way I can think of to fix this is to simply
4538 disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am
4539 not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere.
4540 */
4541 /*#define MA_USE_RUNTIME_LINKING_FOR_PTHREAD*/
4542 
4543 /* Disable run-time linking on certain backends. */
4544 #ifndef MA_NO_RUNTIME_LINKING
4545  #if defined(MA_ANDROID) || defined(MA_EMSCRIPTEN)
4546  #define MA_NO_RUNTIME_LINKING
4547  #endif
4548 #endif
4549 
4550 /*
4551 Check if we have the necessary development packages for each backend at the top so we can use this to determine whether or not
4552 certain unused functions and variables can be excluded from the build to avoid warnings.
4553 */
4554 #ifdef MA_ENABLE_WASAPI
4555  #define MA_HAS_WASAPI /* Every compiler should support WASAPI */
4556 #endif
4557 #ifdef MA_ENABLE_DSOUND
4558  #define MA_HAS_DSOUND /* Every compiler should support DirectSound. */
4559 #endif
4560 #ifdef MA_ENABLE_WINMM
4561  #define MA_HAS_WINMM /* Every compiler I'm aware of supports WinMM. */
4562 #endif
4563 #ifdef MA_ENABLE_ALSA
4564  #define MA_HAS_ALSA
4565  #ifdef MA_NO_RUNTIME_LINKING
4566  #ifdef __has_include
4567  #if !__has_include(<alsa/asoundlib.h>)
4568  #undef MA_HAS_ALSA
4569  #endif
4570  #endif
4571  #endif
4572 #endif
4573 #ifdef MA_ENABLE_PULSEAUDIO
4574  #define MA_HAS_PULSEAUDIO
4575  #ifdef MA_NO_RUNTIME_LINKING
4576  #ifdef __has_include
4577  #if !__has_include(<pulse/pulseaudio.h>)
4578  #undef MA_HAS_PULSEAUDIO
4579  #endif
4580  #endif
4581  #endif
4582 #endif
4583 #ifdef MA_ENABLE_JACK
4584  #define MA_HAS_JACK
4585  #ifdef MA_NO_RUNTIME_LINKING
4586  #ifdef __has_include
4587  #if !__has_include(<jack/jack.h>)
4588  #undef MA_HAS_JACK
4589  #endif
4590  #endif
4591  #endif
4592 #endif
4593 #ifdef MA_ENABLE_COREAUDIO
4594  #define MA_HAS_COREAUDIO
4595 #endif
4596 #ifdef MA_ENABLE_SNDIO
4597  #define MA_HAS_SNDIO
4598 #endif
4599 #ifdef MA_ENABLE_AUDIO4
4600  #define MA_HAS_AUDIO4
4601 #endif
4602 #ifdef MA_ENABLE_OSS
4603  #define MA_HAS_OSS
4604 #endif
4605 #ifdef MA_ENABLE_AAUDIO
4606  #define MA_HAS_AAUDIO
4607 #endif
4608 #ifdef MA_ENABLE_OPENSL
4609  #define MA_HAS_OPENSL
4610 #endif
4611 #ifdef MA_ENABLE_WEBAUDIO
4612  #define MA_HAS_WEBAUDIO
4613 #endif
4614 #ifdef MA_ENABLE_NULL
4615  #define MA_HAS_NULL /* Everything supports the null backend. */
4616 #endif
4617 
4618 const char* ma_get_backend_name(ma_backend backend)
4619 {
4620  switch (backend)
4621  {
4622  case ma_backend_wasapi: return "WASAPI";
4623  case ma_backend_dsound: return "DirectSound";
4624  case ma_backend_winmm: return "WinMM";
4625  case ma_backend_coreaudio: return "Core Audio";
4626  case ma_backend_sndio: return "sndio";
4627  case ma_backend_audio4: return "audio(4)";
4628  case ma_backend_oss: return "OSS";
4629  case ma_backend_pulseaudio: return "PulseAudio";
4630  case ma_backend_alsa: return "ALSA";
4631  case ma_backend_jack: return "JACK";
4632  case ma_backend_aaudio: return "AAudio";
4633  case ma_backend_opensl: return "OpenSL|ES";
4634  case ma_backend_webaudio: return "Web Audio";
4635  case ma_backend_null: return "Null";
4636  default: return "Unknown";
4637  }
4638 }
4639 
4641 {
4642  switch (backend)
4643  {
4644  case ma_backend_wasapi: return MA_TRUE;
4645  case ma_backend_dsound: return MA_FALSE;
4646  case ma_backend_winmm: return MA_FALSE;
4647  case ma_backend_coreaudio: return MA_FALSE;
4648  case ma_backend_sndio: return MA_FALSE;
4649  case ma_backend_audio4: return MA_FALSE;
4650  case ma_backend_oss: return MA_FALSE;
4651  case ma_backend_pulseaudio: return MA_FALSE;
4652  case ma_backend_alsa: return MA_FALSE;
4653  case ma_backend_jack: return MA_FALSE;
4654  case ma_backend_aaudio: return MA_FALSE;
4655  case ma_backend_opensl: return MA_FALSE;
4656  case ma_backend_webaudio: return MA_FALSE;
4657  case ma_backend_null: return MA_FALSE;
4658  default: return MA_FALSE;
4659  }
4660 }
4661 
4662 
4663 
4664 #ifdef MA_WIN32
4665  #define MA_THREADCALL WINAPI
4666  typedef unsigned long ma_thread_result;
4667 #else
4668  #define MA_THREADCALL
4669  typedef void* ma_thread_result;
4670 #endif
4671 typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData);
4672 
4673 #ifdef MA_WIN32
4674 typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit);
4675 typedef void (WINAPI * MA_PFN_CoUninitialize)();
4676 typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv);
4677 typedef void (WINAPI * MA_PFN_CoTaskMemFree)(LPVOID pv);
4678 typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(PROPVARIANT *pvar);
4679 typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax);
4680 
4681 typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)();
4682 typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)();
4683 
4684 /* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */
4685 typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult);
4686 typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey);
4687 typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);
4688 #endif
4689 
4690 
4691 #define MA_STATE_UNINITIALIZED 0
4692 #define MA_STATE_STOPPED 1 /* The device's default state after initialization. */
4693 #define MA_STATE_STARTED 2 /* The worker thread is in it's main loop waiting for the driver to request or deliver audio data. */
4694 #define MA_STATE_STARTING 3 /* Transitioning from a stopped state to started. */
4695 #define MA_STATE_STOPPING 4 /* Transitioning from a started state to stopped. */
4696 
4697 #define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device"
4698 #define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device"
4699 
4700 
4701 const char* ma_log_level_to_string(ma_uint32 logLevel)
4702 {
4703  switch (logLevel)
4704  {
4705  case MA_LOG_LEVEL_VERBOSE: return "";
4706  case MA_LOG_LEVEL_INFO: return "INFO";
4707  case MA_LOG_LEVEL_WARNING: return "WARNING";
4708  case MA_LOG_LEVEL_ERROR: return "ERROR";
4709  default: return "ERROR";
4710  }
4711 }
4712 
4713 /* Posts a log message. */
4714 void ma_log(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
4715 {
4716  if (pContext == NULL) {
4717  return;
4718  }
4719 
4720 #if defined(MA_LOG_LEVEL)
4721  if (logLevel <= MA_LOG_LEVEL) {
4722  ma_log_proc onLog;
4723 
4724  #if defined(MA_DEBUG_OUTPUT)
4725  if (logLevel <= MA_LOG_LEVEL) {
4726  printf("%s: %s\n", ma_log_level_to_string(logLevel), message);
4727  }
4728  #endif
4729 
4730  onLog = pContext->logCallback;
4731  if (onLog) {
4732  onLog(pContext, pDevice, logLevel, message);
4733  }
4734  }
4735 #endif
4736 }
4737 
4738 /* Posts an log message. Throw a breakpoint in here if you're needing to debug. The return value is always "resultCode". */
4739 ma_result ma_context_post_error(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode)
4740 {
4741  /* Derive the context from the device if necessary. */
4742  if (pContext == NULL) {
4743  if (pDevice != NULL) {
4744  pContext = pDevice->pContext;
4745  }
4746  }
4747 
4748  ma_log(pContext, pDevice, logLevel, message);
4749  return resultCode;
4750 }
4751 
4752 ma_result ma_post_error(ma_device* pDevice, ma_uint32 logLevel, const char* message, ma_result resultCode)
4753 {
4754  return ma_context_post_error(NULL, pDevice, logLevel, message, resultCode);
4755 }
4756 
4757 
4758 /*******************************************************************************
4759 
4760 Timing
4761 
4762 *******************************************************************************/
4763 #ifdef MA_WIN32
4764 LARGE_INTEGER g_ma_TimerFrequency = {{0}};
4765 void ma_timer_init(ma_timer* pTimer)
4766 {
4767  LARGE_INTEGER counter;
4768 
4769  if (g_ma_TimerFrequency.QuadPart == 0) {
4770  QueryPerformanceFrequency(&g_ma_TimerFrequency);
4771  }
4772 
4773  QueryPerformanceCounter(&counter);
4774  pTimer->counter = counter.QuadPart;
4775 }
4776 
4777 double ma_timer_get_time_in_seconds(ma_timer* pTimer)
4778 {
4779  LARGE_INTEGER counter;
4780  if (!QueryPerformanceCounter(&counter)) {
4781  return 0;
4782  }
4783 
4784  return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
4785 }
4786 #elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
4787 ma_uint64 g_ma_TimerFrequency = 0;
4788 void ma_timer_init(ma_timer* pTimer)
4789 {
4790  mach_timebase_info_data_t baseTime;
4791  mach_timebase_info(&baseTime);
4792  g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;
4793 
4794  pTimer->counter = mach_absolute_time();
4795 }
4796 
4797 double ma_timer_get_time_in_seconds(ma_timer* pTimer)
4798 {
4799  ma_uint64 newTimeCounter = mach_absolute_time();
4800  ma_uint64 oldTimeCounter = pTimer->counter;
4801 
4802  return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
4803 }
4804 #elif defined(MA_EMSCRIPTEN)
4805 void ma_timer_init(ma_timer* pTimer)
4806 {
4807  pTimer->counterD = emscripten_get_now();
4808 }
4809 
4810 double ma_timer_get_time_in_seconds(ma_timer* pTimer)
4811 {
4812  return (emscripten_get_now() - pTimer->counterD) / 1000; /* Emscripten is in milliseconds. */
4813 }
4814 #else
4815 #if _POSIX_C_SOURCE >= 199309L
4816 #if defined(CLOCK_MONOTONIC)
4817  #define MA_CLOCK_ID CLOCK_MONOTONIC
4818 #else
4819  #define MA_CLOCK_ID CLOCK_REALTIME
4820 #endif
4821 
4822 void ma_timer_init(ma_timer* pTimer)
4823 {
4824  struct timespec newTime;
4825  clock_gettime(MA_CLOCK_ID, &newTime);
4826 
4827  pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
4828 }
4829 
4830 double ma_timer_get_time_in_seconds(ma_timer* pTimer)
4831 {
4832  ma_uint64 newTimeCounter;
4833  ma_uint64 oldTimeCounter;
4834 
4835  struct timespec newTime;
4836  clock_gettime(MA_CLOCK_ID, &newTime);
4837 
4838  newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
4839  oldTimeCounter = pTimer->counter;
4840 
4841  return (newTimeCounter - oldTimeCounter) / 1000000000.0;
4842 }
4843 #else
4844 void ma_timer_init(ma_timer* pTimer)
4845 {
4846  struct timeval newTime;
4847  gettimeofday(&newTime, NULL);
4848 
4849  pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
4850 }
4851 
4852 double ma_timer_get_time_in_seconds(ma_timer* pTimer)
4853 {
4854  ma_uint64 newTimeCounter;
4855  ma_uint64 oldTimeCounter;
4856 
4857  struct timeval newTime;
4858  gettimeofday(&newTime, NULL);
4859 
4860  newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
4861  oldTimeCounter = pTimer->counter;
4862 
4863  return (newTimeCounter - oldTimeCounter) / 1000000.0;
4864 }
4865 #endif
4866 #endif
4867 
4868 
4869 /*******************************************************************************
4870 
4871 Dynamic Linking
4872 
4873 *******************************************************************************/
4874 ma_handle ma_dlopen(ma_context* pContext, const char* filename)
4875 {
4876  ma_handle handle;
4877 
4878 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE
4879  if (pContext != NULL) {
4880  char message[256];
4881  ma_strappend(message, sizeof(message), "Loading library: ", filename);
4882  ma_log(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message);
4883  }
4884 #endif
4885 
4886 #ifdef _WIN32
4887 #ifdef MA_WIN32_DESKTOP
4888  handle = (ma_handle)LoadLibraryA(filename);
4889 #else
4890  /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */
4891  WCHAR filenameW[4096];
4892  if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) {
4893  handle = NULL;
4894  } else {
4895  handle = (ma_handle)LoadPackagedLibrary(filenameW, 0);
4896  }
4897 #endif
4898 #else
4899  handle = (ma_handle)dlopen(filename, RTLD_NOW);
4900 #endif
4901 
4902  /*
4903  I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority
4904  backend is a deliberate design choice. Instead I'm logging it as an informational message.
4905  */
4906 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_INFO
4907  if (handle == NULL) {
4908  char message[256];
4909  ma_strappend(message, sizeof(message), "Failed to load library: ", filename);
4910  ma_log(pContext, NULL, MA_LOG_LEVEL_INFO, message);
4911  }
4912 #endif
4913 
4914  (void)pContext; /* It's possible for pContext to be unused. */
4915  return handle;
4916 }
4917 
4918 void ma_dlclose(ma_context* pContext, ma_handle handle)
4919 {
4920 #ifdef _WIN32
4921  FreeLibrary((HMODULE)handle);
4922 #else
4923  dlclose((void*)handle);
4924 #endif
4925 
4926  (void)pContext;
4927 }
4928 
4929 ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol)
4930 {
4931  ma_proc proc;
4932 
4933 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_VERBOSE
4934  if (pContext != NULL) {
4935  char message[256];
4936  ma_strappend(message, sizeof(message), "Loading symbol: ", symbol);
4937  ma_log(pContext, NULL, MA_LOG_LEVEL_VERBOSE, message);
4938  }
4939 #endif
4940 
4941 #ifdef _WIN32
4942  proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol);
4943 #else
4944 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
4945  #pragma GCC diagnostic push
4946  #pragma GCC diagnostic ignored "-Wpedantic"
4947 #endif
4948  proc = (ma_proc)dlsym((void*)handle, symbol);
4949 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
4950  #pragma GCC diagnostic pop
4951 #endif
4952 #endif
4953 
4954 #if MA_LOG_LEVEL >= MA_LOG_LEVEL_WARNING
4955  if (handle == NULL) {
4956  char message[256];
4957  ma_strappend(message, sizeof(message), "Failed to load symbol: ", symbol);
4958  ma_log(pContext, NULL, MA_LOG_LEVEL_WARNING, message);
4959  }
4960 #endif
4961 
4962  (void)pContext; /* It's possible for pContext to be unused. */
4963  return proc;
4964 }
4965 
4966 
4967 /*******************************************************************************
4968 
4969 Threading
4970 
4971 *******************************************************************************/
4972 #ifdef MA_WIN32
4973 int ma_thread_priority_to_win32(ma_thread_priority priority)
4974 {
4975  switch (priority) {
4976  case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE;
4977  case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST;
4978  case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL;
4979  case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL;
4980  case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL;
4981  case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST;
4982  case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL;
4983  default: return THREAD_PRIORITY_NORMAL;
4984  }
4985 }
4986 
4987 ma_result ma_thread_create__win32(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
4988 {
4989  pThread->win32.hThread = CreateThread(NULL, 0, entryProc, pData, 0, NULL);
4990  if (pThread->win32.hThread == NULL) {
4992  }
4993 
4994  SetThreadPriority((HANDLE)pThread->win32.hThread, ma_thread_priority_to_win32(pContext->threadPriority));
4995 
4996  return MA_SUCCESS;
4997 }
4998 
4999 void ma_thread_wait__win32(ma_thread* pThread)
5000 {
5001  WaitForSingleObject(pThread->win32.hThread, INFINITE);
5002 }
5003 
5004 void ma_sleep__win32(ma_uint32 milliseconds)
5005 {
5006  Sleep((DWORD)milliseconds);
5007 }
5008 
5009 
5010 ma_result ma_mutex_init__win32(ma_context* pContext, ma_mutex* pMutex)
5011 {
5012  (void)pContext;
5013 
5014  pMutex->win32.hMutex = CreateEventA(NULL, FALSE, TRUE, NULL);
5015  if (pMutex->win32.hMutex == NULL) {
5017  }
5018 
5019  return MA_SUCCESS;
5020 }
5021 
5022 void ma_mutex_uninit__win32(ma_mutex* pMutex)
5023 {
5024  CloseHandle(pMutex->win32.hMutex);
5025 }
5026 
5027 void ma_mutex_lock__win32(ma_mutex* pMutex)
5028 {
5029  WaitForSingleObject(pMutex->win32.hMutex, INFINITE);
5030 }
5031 
5032 void ma_mutex_unlock__win32(ma_mutex* pMutex)
5033 {
5034  SetEvent(pMutex->win32.hMutex);
5035 }
5036 
5037 
5038 ma_result ma_event_init__win32(ma_context* pContext, ma_event* pEvent)
5039 {
5040  (void)pContext;
5041 
5042  pEvent->win32.hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
5043  if (pEvent->win32.hEvent == NULL) {
5045  }
5046 
5047  return MA_SUCCESS;
5048 }
5049 
5050 void ma_event_uninit__win32(ma_event* pEvent)
5051 {
5052  CloseHandle(pEvent->win32.hEvent);
5053 }
5054 
5055 ma_bool32 ma_event_wait__win32(ma_event* pEvent)
5056 {
5057  return WaitForSingleObject(pEvent->win32.hEvent, INFINITE) == WAIT_OBJECT_0;
5058 }
5059 
5060 ma_bool32 ma_event_signal__win32(ma_event* pEvent)
5061 {
5062  return SetEvent(pEvent->win32.hEvent);
5063 }
5064 #endif
5065 
5066 
5067 #ifdef MA_POSIX
5068 #include <sched.h>
5069 
5070 typedef int (* ma_pthread_create_proc)(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
5071 typedef int (* ma_pthread_join_proc)(pthread_t thread, void **retval);
5072 typedef int (* ma_pthread_mutex_init_proc)(pthread_mutex_t *__mutex, const pthread_mutexattr_t *__mutexattr);
5073 typedef int (* ma_pthread_mutex_destroy_proc)(pthread_mutex_t *__mutex);
5074 typedef int (* ma_pthread_mutex_lock_proc)(pthread_mutex_t *__mutex);
5075 typedef int (* ma_pthread_mutex_unlock_proc)(pthread_mutex_t *__mutex);
5076 typedef int (* ma_pthread_cond_init_proc)(pthread_cond_t *__restrict __cond, const pthread_condattr_t *__restrict __cond_attr);
5077 typedef int (* ma_pthread_cond_destroy_proc)(pthread_cond_t *__cond);
5078 typedef int (* ma_pthread_cond_signal_proc)(pthread_cond_t *__cond);
5079 typedef int (* ma_pthread_cond_wait_proc)(pthread_cond_t *__restrict __cond, pthread_mutex_t *__restrict __mutex);
5080 typedef int (* ma_pthread_attr_init_proc)(pthread_attr_t *attr);
5081 typedef int (* ma_pthread_attr_destroy_proc)(pthread_attr_t *attr);
5082 typedef int (* ma_pthread_attr_setschedpolicy_proc)(pthread_attr_t *attr, int policy);
5083 typedef int (* ma_pthread_attr_getschedparam_proc)(const pthread_attr_t *attr, struct sched_param *param);
5084 typedef int (* ma_pthread_attr_setschedparam_proc)(pthread_attr_t *attr, const struct sched_param *param);
5085 
5086 ma_result ma_thread_create__posix(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
5087 {
5088  int result;
5089  pthread_attr_t* pAttr = NULL;
5090 
5091 #if !defined(__EMSCRIPTEN__)
5092  /* Try setting the thread priority. It's not critical if anything fails here. */
5093  pthread_attr_t attr;
5094  if (((ma_pthread_attr_init_proc)pContext->posix.pthread_attr_init)(&attr) == 0) {
5095  int scheduler = -1;
5096  if (pContext->threadPriority == ma_thread_priority_idle) {
5097 #ifdef SCHED_IDLE
5098  if (((ma_pthread_attr_setschedpolicy_proc)pContext->posix.pthread_attr_setschedpolicy)(&attr, SCHED_IDLE) == 0) {
5099  scheduler = SCHED_IDLE;
5100  }
5101 #endif
5102  } else if (pContext->threadPriority == ma_thread_priority_realtime) {
5103 #ifdef SCHED_FIFO
5104  if (((ma_pthread_attr_setschedpolicy_proc)pContext->posix.pthread_attr_setschedpolicy)(&attr, SCHED_FIFO) == 0) {
5105  scheduler = SCHED_FIFO;
5106  }
5107 #endif
5108 #ifdef MA_LINUX
5109  } else {
5110  scheduler = sched_getscheduler(0);
5111 #endif
5112  }
5113 
5114  if (scheduler != -1) {
5115  int priorityMin = sched_get_priority_min(scheduler);
5116  int priorityMax = sched_get_priority_max(scheduler);
5117  int priorityStep = (priorityMax - priorityMin) / 7; /* 7 = number of priorities supported by miniaudio. */
5118 
5119  struct sched_param sched;
5120  if (((ma_pthread_attr_getschedparam_proc)pContext->posix.pthread_attr_getschedparam)(&attr, &sched) == 0) {
5121  if (pContext->threadPriority == ma_thread_priority_idle) {
5122  sched.sched_priority = priorityMin;
5123  } else if (pContext->threadPriority == ma_thread_priority_realtime) {
5124  sched.sched_priority = priorityMax;
5125  } else {
5126  sched.sched_priority += ((int)pContext->threadPriority + 5) * priorityStep; /* +5 because the lowest priority is -5. */
5127  if (sched.sched_priority < priorityMin) {
5128  sched.sched_priority = priorityMin;
5129  }
5130  if (sched.sched_priority > priorityMax) {
5131  sched.sched_priority = priorityMax;
5132  }
5133  }
5134 
5135  if (((ma_pthread_attr_setschedparam_proc)pContext->posix.pthread_attr_setschedparam)(&attr, &sched) == 0) {
5136  pAttr = &attr;
5137  }
5138  }
5139  }
5140 
5141  ((ma_pthread_attr_destroy_proc)pContext->posix.pthread_attr_destroy)(&attr);
5142  }
5143 #endif
5144 
5145  result = ((ma_pthread_create_proc)pContext->posix.pthread_create)(&pThread->posix.thread, pAttr, entryProc, pData);
5146  if (result != 0) {
5148  }
5149 
5150  return MA_SUCCESS;
5151 }
5152 
5153 void ma_thread_wait__posix(ma_thread* pThread)
5154 {
5155  ((ma_pthread_join_proc)pThread->pContext->posix.pthread_join)(pThread->posix.thread, NULL);
5156 }
5157 
5158 void ma_sleep__posix(ma_uint32 milliseconds)
5159 {
5160 #ifdef MA_EMSCRIPTEN
5161  (void)milliseconds;
5162  ma_assert(MA_FALSE); /* The Emscripten build should never sleep. */
5163 #else
5164  #if _POSIX_C_SOURCE >= 199309L
5165  struct timespec ts;
5166  ts.tv_sec = milliseconds / 1000000;
5167  ts.tv_nsec = milliseconds % 1000000 * 1000000;
5168  nanosleep(&ts, NULL);
5169  #else
5170  struct timeval tv;
5171  tv.tv_sec = milliseconds / 1000;
5172  tv.tv_usec = milliseconds % 1000 * 1000;
5173  select(0, NULL, NULL, NULL, &tv);
5174  #endif
5175 #endif
5176 }
5177 
5178 
5179 ma_result ma_mutex_init__posix(ma_context* pContext, ma_mutex* pMutex)
5180 {
5181  int result = ((ma_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pMutex->posix.mutex, NULL);
5182  if (result != 0) {
5184  }
5185 
5186  return MA_SUCCESS;
5187 }
5188 
5189 void ma_mutex_uninit__posix(ma_mutex* pMutex)
5190 {
5191  ((ma_pthread_mutex_destroy_proc)pMutex->pContext->posix.pthread_mutex_destroy)(&pMutex->posix.mutex);
5192 }
5193 
5194 void ma_mutex_lock__posix(ma_mutex* pMutex)
5195 {
5196  ((ma_pthread_mutex_lock_proc)pMutex->pContext->posix.pthread_mutex_lock)(&pMutex->posix.mutex);
5197 }
5198 
5199 void ma_mutex_unlock__posix(ma_mutex* pMutex)
5200 {
5201  ((ma_pthread_mutex_unlock_proc)pMutex->pContext->posix.pthread_mutex_unlock)(&pMutex->posix.mutex);
5202 }
5203 
5204 
5205 ma_result ma_event_init__posix(ma_context* pContext, ma_event* pEvent)
5206 {
5207  if (((ma_pthread_mutex_init_proc)pContext->posix.pthread_mutex_init)(&pEvent->posix.mutex, NULL) != 0) {
5209  }
5210 
5211  if (((ma_pthread_cond_init_proc)pContext->posix.pthread_cond_init)(&pEvent->posix.condition, NULL) != 0) {
5213  }
5214 
5215  pEvent->posix.value = 0;
5216  return MA_SUCCESS;
5217 }
5218 
5219 void ma_event_uninit__posix(ma_event* pEvent)
5220 {
5221  ((ma_pthread_cond_destroy_proc)pEvent->pContext->posix.pthread_cond_destroy)(&pEvent->posix.condition);
5222  ((ma_pthread_mutex_destroy_proc)pEvent->pContext->posix.pthread_mutex_destroy)(&pEvent->posix.mutex);
5223 }
5224 
5225 ma_bool32 ma_event_wait__posix(ma_event* pEvent)
5226 {
5227  ((ma_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex);
5228  {
5229  while (pEvent->posix.value == 0) {
5230  ((ma_pthread_cond_wait_proc)pEvent->pContext->posix.pthread_cond_wait)(&pEvent->posix.condition, &pEvent->posix.mutex);
5231  }
5232  pEvent->posix.value = 0; /* Auto-reset. */
5233  }
5234  ((ma_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex);
5235 
5236  return MA_TRUE;
5237 }
5238 
5239 ma_bool32 ma_event_signal__posix(ma_event* pEvent)
5240 {
5241  ((ma_pthread_mutex_lock_proc)pEvent->pContext->posix.pthread_mutex_lock)(&pEvent->posix.mutex);
5242  {
5243  pEvent->posix.value = 1;
5244  ((ma_pthread_cond_signal_proc)pEvent->pContext->posix.pthread_cond_signal)(&pEvent->posix.condition);
5245  }
5246  ((ma_pthread_mutex_unlock_proc)pEvent->pContext->posix.pthread_mutex_unlock)(&pEvent->posix.mutex);
5247 
5248  return MA_TRUE;
5249 }
5250 #endif
5251 
5252 ma_result ma_thread_create(ma_context* pContext, ma_thread* pThread, ma_thread_entry_proc entryProc, void* pData)
5253 {
5254  if (pContext == NULL || pThread == NULL || entryProc == NULL) {
5255  return MA_FALSE;
5256  }
5257 
5258  pThread->pContext = pContext;
5259 
5260 #ifdef MA_WIN32
5261  return ma_thread_create__win32(pContext, pThread, entryProc, pData);
5262 #endif
5263 #ifdef MA_POSIX
5264  return ma_thread_create__posix(pContext, pThread, entryProc, pData);
5265 #endif
5266 }
5267 
5268 void ma_thread_wait(ma_thread* pThread)
5269 {
5270  if (pThread == NULL) {
5271  return;
5272  }
5273 
5274 #ifdef MA_WIN32
5275  ma_thread_wait__win32(pThread);
5276 #endif
5277 #ifdef MA_POSIX
5278  ma_thread_wait__posix(pThread);
5279 #endif
5280 }
5281 
5282 void ma_sleep(ma_uint32 milliseconds)
5283 {
5284 #ifdef MA_WIN32
5285  ma_sleep__win32(milliseconds);
5286 #endif
5287 #ifdef MA_POSIX
5288  ma_sleep__posix(milliseconds);
5289 #endif
5290 }
5291 
5292 
5293 ma_result ma_mutex_init(ma_context* pContext, ma_mutex* pMutex)
5294 {
5295  if (pContext == NULL || pMutex == NULL) {
5296  return MA_INVALID_ARGS;
5297  }
5298 
5299  pMutex->pContext = pContext;
5300 
5301 #ifdef MA_WIN32
5302  return ma_mutex_init__win32(pContext, pMutex);
5303 #endif
5304 #ifdef MA_POSIX
5305  return ma_mutex_init__posix(pContext, pMutex);
5306 #endif
5307 }
5308 
5309 void ma_mutex_uninit(ma_mutex* pMutex)
5310 {
5311  if (pMutex == NULL || pMutex->pContext == NULL) {
5312  return;
5313  }
5314 
5315 #ifdef MA_WIN32
5316  ma_mutex_uninit__win32(pMutex);
5317 #endif
5318 #ifdef MA_POSIX
5319  ma_mutex_uninit__posix(pMutex);
5320 #endif
5321 }
5322 
5323 void ma_mutex_lock(ma_mutex* pMutex)
5324 {
5325  if (pMutex == NULL || pMutex->pContext == NULL) {
5326  return;
5327  }
5328 
5329 #ifdef MA_WIN32
5330  ma_mutex_lock__win32(pMutex);
5331 #endif
5332 #ifdef MA_POSIX
5333  ma_mutex_lock__posix(pMutex);
5334 #endif
5335 }
5336 
5337 void ma_mutex_unlock(ma_mutex* pMutex)
5338 {
5339  if (pMutex == NULL || pMutex->pContext == NULL) {
5340  return;
5341 }
5342 
5343 #ifdef MA_WIN32
5344  ma_mutex_unlock__win32(pMutex);
5345 #endif
5346 #ifdef MA_POSIX
5347  ma_mutex_unlock__posix(pMutex);
5348 #endif
5349 }
5350 
5351 
5352 ma_result ma_event_init(ma_context* pContext, ma_event* pEvent)
5353 {
5354  if (pContext == NULL || pEvent == NULL) {
5355  return MA_FALSE;
5356  }
5357 
5358  pEvent->pContext = pContext;
5359 
5360 #ifdef MA_WIN32
5361  return ma_event_init__win32(pContext, pEvent);
5362 #endif
5363 #ifdef MA_POSIX
5364  return ma_event_init__posix(pContext, pEvent);
5365 #endif
5366 }
5367 
5368 void ma_event_uninit(ma_event* pEvent)
5369 {
5370  if (pEvent == NULL || pEvent->pContext == NULL) {
5371  return;
5372  }
5373 
5374 #ifdef MA_WIN32
5375  ma_event_uninit__win32(pEvent);
5376 #endif
5377 #ifdef MA_POSIX
5378  ma_event_uninit__posix(pEvent);
5379 #endif
5380 }
5381 
5382 ma_bool32 ma_event_wait(ma_event* pEvent)
5383 {
5384  if (pEvent == NULL || pEvent->pContext == NULL) {
5385  return MA_FALSE;
5386  }
5387 
5388 #ifdef MA_WIN32
5389  return ma_event_wait__win32(pEvent);
5390 #endif
5391 #ifdef MA_POSIX
5392  return ma_event_wait__posix(pEvent);
5393 #endif
5394 }
5395 
5396 ma_bool32 ma_event_signal(ma_event* pEvent)
5397 {
5398  if (pEvent == NULL || pEvent->pContext == NULL) {
5399  return MA_FALSE;
5400  }
5401 
5402 #ifdef MA_WIN32
5403  return ma_event_signal__win32(pEvent);
5404 #endif
5405 #ifdef MA_POSIX
5406  return ma_event_signal__posix(pEvent);
5407 #endif
5408 }
5409 
5410 
5411 ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax)
5412 {
5413  /* Normalize the range in case we were given something stupid. */
5414  if (sampleRateMin < MA_MIN_SAMPLE_RATE) {
5415  sampleRateMin = MA_MIN_SAMPLE_RATE;
5416  }
5417  if (sampleRateMax > MA_MAX_SAMPLE_RATE) {
5418  sampleRateMax = MA_MAX_SAMPLE_RATE;
5419  }
5420  if (sampleRateMin > sampleRateMax) {
5421  sampleRateMin = sampleRateMax;
5422  }
5423 
5424  if (sampleRateMin == sampleRateMax) {
5425  return sampleRateMax;
5426  } else {
5427  size_t iStandardRate;
5428  for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
5429  ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
5430  if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) {
5431  return standardRate;
5432  }
5433  }
5434  }
5435 
5436  /* Should never get here. */
5437  ma_assert(MA_FALSE);
5438  return 0;
5439 }
5440 
5441 ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn)
5442 {
5443  ma_uint32 closestRate = 0;
5444  ma_uint32 closestDiff = 0xFFFFFFFF;
5445  size_t iStandardRate;
5446 
5447  for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
5448  ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
5449  ma_uint32 diff;
5450 
5451  if (sampleRateIn > standardRate) {
5452  diff = sampleRateIn - standardRate;
5453  } else {
5454  diff = standardRate - sampleRateIn;
5455  }
5456 
5457  if (diff == 0) {
5458  return standardRate; /* The input sample rate is a standard rate. */
5459  }
5460 
5461  if (closestDiff > diff) {
5462  closestDiff = diff;
5463  closestRate = standardRate;
5464  }
5465  }
5466 
5467  return closestRate;
5468 }
5469 
5470 
5471 ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale)
5472 {
5473  return ma_max(1, (ma_uint32)(baseBufferSize*scale));
5474 }
5475 
5477 {
5478  return bufferSizeInFrames / (sampleRate/1000);
5479 }
5480 
5482 {
5483  return bufferSizeInMilliseconds * (sampleRate/1000);
5484 }
5485 
5487 {
5488  if (performanceProfile == ma_performance_profile_low_latency) {
5489  return MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY;
5490  } else {
5491  return MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE;
5492  }
5493 }
5494 
5496 {
5497  ma_uint32 bufferSizeInMilliseconds;
5498  ma_uint32 sampleRateMS;
5499 
5500  bufferSizeInMilliseconds = ma_get_default_buffer_size_in_milliseconds(performanceProfile);
5501  if (bufferSizeInMilliseconds == 0) {
5502  bufferSizeInMilliseconds = 1;
5503  }
5504 
5505  sampleRateMS = (sampleRate/1000);
5506  if (sampleRateMS == 0) {
5507  sampleRateMS = 1;
5508  }
5509 
5510  return bufferSizeInMilliseconds * sampleRateMS;
5511 }
5512 
5513 ma_uint32 ma_get_fragment_size_in_bytes(ma_uint32 bufferSizeInFrames, ma_uint32 periods, ma_format format, ma_uint32 channels)
5514 {
5515  ma_uint32 fragmentSizeInFrames = bufferSizeInFrames / periods;
5516  return fragmentSizeInFrames * ma_get_bytes_per_frame(format, channels);
5517 }
5518 
5519 void ma_zero_pcm_frames(void* p, ma_uint32 frameCount, ma_format format, ma_uint32 channels)
5520 {
5521  ma_zero_memory(p, frameCount * ma_get_bytes_per_frame(format, channels));
5522 }
5523 
5524 void ma_clip_samples_f32(float* p, ma_uint32 sampleCount)
5525 {
5526  ma_uint32 iSample;
5527 
5528  /* TODO: Research a branchless SSE implementation. */
5529  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5530  p[iSample] = ma_clip_f32(p[iSample]);
5531  }
5532 }
5533 
5534 
5535 void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint32 sampleCount, float factor)
5536 {
5537  ma_uint32 iSample;
5538 
5539  if (pSamplesOut == NULL || pSamplesIn == NULL) {
5540  return;
5541  }
5542 
5543  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5544  pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor);
5545  }
5546 }
5547 
5548 void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint32 sampleCount, float factor)
5549 {
5550  ma_uint32 iSample;
5551 
5552  if (pSamplesOut == NULL || pSamplesIn == NULL) {
5553  return;
5554  }
5555 
5556  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5557  pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor);
5558  }
5559 }
5560 
5561 void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint32 sampleCount, float factor)
5562 {
5563  ma_uint32 iSample;
5564  ma_uint8* pSamplesOut8;
5565  ma_uint8* pSamplesIn8;
5566 
5567  if (pSamplesOut == NULL || pSamplesIn == NULL) {
5568  return;
5569  }
5570 
5571  pSamplesOut8 = (ma_uint8*)pSamplesOut;
5572  pSamplesIn8 = (ma_uint8*)pSamplesIn;
5573 
5574  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5575  ma_int32 sampleS32;
5576 
5577  sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24);
5578  sampleS32 = (ma_int32)(sampleS32 * factor);
5579 
5580  pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8);
5581  pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16);
5582  pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24);
5583  }
5584 }
5585 
5586 void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint32 sampleCount, float factor)
5587 {
5588  ma_uint32 iSample;
5589 
5590  if (pSamplesOut == NULL || pSamplesIn == NULL) {
5591  return;
5592  }
5593 
5594  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5595  pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor);
5596  }
5597 }
5598 
5599 void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint32 sampleCount, float factor)
5600 {
5601  ma_uint32 iSample;
5602 
5603  if (pSamplesOut == NULL || pSamplesIn == NULL) {
5604  return;
5605  }
5606 
5607  for (iSample = 0; iSample < sampleCount; iSample += 1) {
5608  pSamplesOut[iSample] = pSamplesIn[iSample] * factor;
5609  }
5610 }
5611 
5612 void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint32 sampleCount, float factor)
5613 {
5614  ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor);
5615 }
5616 
5617 void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint32 sampleCount, float factor)
5618 {
5619  ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor);
5620 }
5621 
5622 void ma_apply_volume_factor_s24(void* pSamples, ma_uint32 sampleCount, float factor)
5623 {
5624  ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor);
5625 }
5626 
5627 void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint32 sampleCount, float factor)
5628 {
5629  ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor);
5630 }
5631 
5632 void ma_apply_volume_factor_f32(float* pSamples, ma_uint32 sampleCount, float factor)
5633 {
5634  ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor);
5635 }
5636 
5637 void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFramesOut, const ma_uint8* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
5638 {
5639  ma_copy_and_apply_volume_factor_u8(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
5640 }
5641 
5642 void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFramesOut, const ma_int16* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
5643 {
5644  ma_copy_and_apply_volume_factor_s16(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
5645 }
5646 
5647 void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
5648 {
5649  ma_copy_and_apply_volume_factor_s24(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
5650 }
5651 
5652 void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFramesOut, const ma_int32* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
5653 {
5654  ma_copy_and_apply_volume_factor_s32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
5655 }
5656 
5657 void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pPCMFramesOut, const float* pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
5658 {
5659  ma_copy_and_apply_volume_factor_f32(pPCMFramesOut, pPCMFramesIn, frameCount*channels, factor);
5660 }
5661 
5662 void ma_copy_and_apply_volume_factor_pcm_frames(void* pPCMFramesOut, const void* pPCMFramesIn, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor)
5663 {
5664  switch (format)
5665  {
5666  case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pPCMFramesOut, (const ma_uint8*)pPCMFramesIn, frameCount, channels, factor); return;
5667  case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pPCMFramesOut, (const ma_int16*)pPCMFramesIn, frameCount, channels, factor); return;
5668  case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pPCMFramesOut, pPCMFramesIn, frameCount, channels, factor); return;
5669  case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pPCMFramesOut, (const ma_int32*)pPCMFramesIn, frameCount, channels, factor); return;
5670  case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pPCMFramesOut, (const float*)pPCMFramesIn, frameCount, channels, factor); return;
5671  default: return; /* Do nothing. */
5672  }
5673 }
5674 
5675 void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
5676 {
5677  ma_copy_and_apply_volume_factor_pcm_frames_u8(pPCMFrames, pPCMFrames, frameCount, channels, factor);
5678 }
5679 
5680 void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
5681 {
5682  ma_copy_and_apply_volume_factor_pcm_frames_s16(pPCMFrames, pPCMFrames, frameCount, channels, factor);
5683 }
5684 
5685 void ma_apply_volume_factor_pcm_frames_s24(void* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
5686 {
5687  ma_copy_and_apply_volume_factor_pcm_frames_s24(pPCMFrames, pPCMFrames, frameCount, channels, factor);
5688 }
5689 
5690 void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
5691 {
5692  ma_copy_and_apply_volume_factor_pcm_frames_s32(pPCMFrames, pPCMFrames, frameCount, channels, factor);
5693 }
5694 
5695 void ma_apply_volume_factor_pcm_frames_f32(float* pPCMFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
5696 {
5697  ma_copy_and_apply_volume_factor_pcm_frames_f32(pPCMFrames, pPCMFrames, frameCount, channels, factor);
5698 }
5699 
5700 void ma_apply_volume_factor_pcm_frames(void* pPCMFrames, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor)
5701 {
5702  ma_copy_and_apply_volume_factor_pcm_frames(pPCMFrames, pPCMFrames, frameCount, format, channels, factor);
5703 }
5704 
5705 
5706 float ma_factor_to_gain_db(float factor)
5707 {
5708  return (float)(20*log10(factor));
5709 }
5710 
5711 float ma_gain_db_to_factor(float gain)
5712 {
5713  return (float)pow(10, gain/20.0);
5714 }
5715 
5716 
5717 static MA_INLINE void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
5718 {
5719  ma_device_callback_proc onData;
5720 
5721  onData = pDevice->onData;
5722  if (onData) {
5723  if (!pDevice->noPreZeroedOutputBuffer && pFramesOut != NULL) {
5724  ma_zero_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels);
5725  }
5726 
5727  /* Volume control of input makes things a bit awkward because the input buffer is read-only. We'll need to use a temp buffer and loop in this case. */
5728  if (pFramesIn != NULL && pDevice->masterVolumeFactor < 1) {
5729  ma_uint8 tempFramesIn[8192];
5730  ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
5731  ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
5732  ma_uint32 totalFramesProcessed = 0;
5733  while (totalFramesProcessed < frameCount) {
5734  ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed;
5735  if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) {
5736  framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture;
5737  }
5738 
5739  ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, pDevice->masterVolumeFactor);
5740 
5741  onData(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration);
5742 
5743  totalFramesProcessed += framesToProcessThisIteration;
5744  }
5745  } else {
5746  onData(pDevice, pFramesOut, pFramesIn, frameCount);
5747  }
5748 
5749  /* Volume control and clipping for playback devices. */
5750  if (pFramesOut != NULL) {
5751  if (pDevice->masterVolumeFactor < 1) {
5752  if (pFramesIn == NULL) { /* <-- In full-duplex situations, the volume will have been applied to the input samples before the data callback. Applying it again post-callback will incorrectly compound it. */
5753  ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, pDevice->masterVolumeFactor);
5754  }
5755  }
5756 
5757  if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) {
5758  ma_clip_pcm_frames_f32((float*)pFramesOut, frameCount, pDevice->playback.channels);
5759  }
5760  }
5761  }
5762 }
5763 
5764 
5765 /* The callback for reading from the client -> DSP -> device. */
5766 ma_uint32 ma_device__on_read_from_client(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint32 frameCount, void* pUserData)
5767 {
5768  ma_device* pDevice = (ma_device*)pUserData;
5769 
5770  ma_assert(pDevice != NULL);
5771 
5772  ma_device__on_data(pDevice, pFramesOut, NULL, frameCount);
5773 
5774  (void)pDSP;
5775  return frameCount;
5776 }
5777 
5778 /* The PCM converter callback for reading from a buffer. */
5779 ma_uint32 ma_device__pcm_converter__on_read_from_buffer_capture(ma_pcm_converter* pConverter, void* pFramesOut, ma_uint32 frameCount, void* pUserData)
5780 {
5781  ma_device* pDevice = (ma_device*)pUserData;
5782  ma_uint32 framesToRead;
5783  ma_uint32 bytesToRead;
5784 
5785  ma_assert(pDevice != NULL);
5786 
5787  if (pDevice->capture._dspFrameCount == 0) {
5788  return 0; /* Nothing left. */
5789  }
5790 
5791  framesToRead = frameCount;
5792  if (framesToRead > pDevice->capture._dspFrameCount) {
5793  framesToRead = pDevice->capture._dspFrameCount;
5794  }
5795 
5796  bytesToRead = framesToRead * ma_get_bytes_per_frame(pConverter->formatConverterIn.config.formatIn, pConverter->channelRouter.config.channelsIn);
5797 
5798  /* pDevice->capture._dspFrames can be null in which case it should be treated as silence. */
5799  if (pDevice->capture._dspFrames != NULL) {
5800  ma_copy_memory(pFramesOut, pDevice->capture._dspFrames, bytesToRead);
5801  pDevice->capture._dspFrames += bytesToRead;
5802  } else {
5803  ma_zero_memory(pFramesOut, bytesToRead);
5804  }
5805 
5806  pDevice->capture._dspFrameCount -= framesToRead;
5807 
5808  return framesToRead;
5809 }
5810 
5811 ma_uint32 ma_device__pcm_converter__on_read_from_buffer_playback(ma_pcm_converter* pConverter, void* pFramesOut, ma_uint32 frameCount, void* pUserData)
5812 {
5813  ma_device* pDevice = (ma_device*)pUserData;
5814  ma_uint32 framesToRead;
5815  ma_uint32 bytesToRead;
5816 
5817  ma_assert(pDevice != NULL);
5818 
5819  if (pDevice->playback._dspFrameCount == 0) {
5820  return 0; /* Nothing left. */
5821  }
5822 
5823  framesToRead = frameCount;
5824  if (framesToRead > pDevice->playback._dspFrameCount) {
5825  framesToRead = pDevice->playback._dspFrameCount;
5826  }
5827 
5828  bytesToRead = framesToRead * ma_get_bytes_per_frame(pConverter->formatConverterIn.config.formatIn, pConverter->channelRouter.config.channelsIn);
5829 
5830  /* pDevice->playback._dspFrames can be null in which case it should be treated as silence. */
5831  if (pDevice->playback._dspFrames != NULL) {
5832  ma_copy_memory(pFramesOut, pDevice->playback._dspFrames, bytesToRead);
5833  pDevice->playback._dspFrames += bytesToRead;
5834  } else {
5835  ma_zero_memory(pFramesOut, bytesToRead);
5836  }
5837 
5838  pDevice->playback._dspFrameCount -= framesToRead;
5839 
5840  return framesToRead;
5841 }
5842 
5843 
5844 
5845 /* A helper function for reading sample data from the client. */
5846 static MA_INLINE void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut)
5847 {
5848  ma_assert(pDevice != NULL);
5849  ma_assert(frameCount > 0);
5850  ma_assert(pFramesOut != NULL);
5851 
5852  if (pDevice->playback.converter.isPassthrough) {
5853  ma_device__on_data(pDevice, pFramesOut, NULL, frameCount);
5854  } else {
5855  ma_pcm_converter_read(&pDevice->playback.converter, pFramesOut, frameCount);
5856  }
5857 }
5858 
5859 /* A helper for sending sample data to the client. */
5860 static MA_INLINE void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCount, const void* pFrames)
5861 {
5862  ma_assert(pDevice != NULL);
5863  ma_assert(frameCount > 0);
5864  ma_assert(pFrames != NULL);
5865 
5866  if (pDevice->capture.converter.isPassthrough) {
5867  ma_device__on_data(pDevice, NULL, pFrames, frameCount);
5868  } else {
5869  ma_uint8 chunkBuffer[4096];
5870  ma_uint32 chunkFrameCount;
5871 
5872  pDevice->capture._dspFrameCount = frameCount;
5873  pDevice->capture._dspFrames = (const ma_uint8*)pFrames;
5874 
5875  chunkFrameCount = sizeof(chunkBuffer) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
5876 
5877  for (;;) {
5878  ma_uint32 framesJustRead = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, chunkBuffer, chunkFrameCount);
5879  if (framesJustRead == 0) {
5880  break;
5881  }
5882 
5883  ma_device__on_data(pDevice, NULL, chunkBuffer, framesJustRead);
5884 
5885  if (framesJustRead < chunkFrameCount) {
5886  break;
5887  }
5888  }
5889  }
5890 }
5891 
5892 static MA_INLINE ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCount, const void* pFramesInInternalFormat, ma_pcm_rb* pRB)
5893 {
5894  ma_result result;
5895 
5896  ma_assert(pDevice != NULL);
5897  ma_assert(frameCount > 0);
5898  ma_assert(pFramesInInternalFormat != NULL);
5899  ma_assert(pRB != NULL);
5900 
5901  pDevice->capture._dspFrameCount = (ma_uint32)frameCount;
5902  pDevice->capture._dspFrames = (const ma_uint8*)pFramesInInternalFormat;
5903 
5904  /* Write to the ring buffer. The ring buffer is in the external format. */
5905  for (;;) {
5906  ma_uint32 framesProcessed;
5907  ma_uint32 framesToProcess = 256;
5908  void* pFramesInExternalFormat;
5909 
5910  result = ma_pcm_rb_acquire_write(pRB, &framesToProcess, &pFramesInExternalFormat);
5911  if (result != MA_SUCCESS) {
5912  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer.", result);
5913  break;
5914  }
5915 
5916  if (framesToProcess == 0) {
5918  break; /* Overrun. Not enough room in the ring buffer for input frame. Excess frames are dropped. */
5919  }
5920  }
5921 
5922  /* Convert. */
5923  framesProcessed = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, pFramesInExternalFormat, framesToProcess);
5924 
5925  result = ma_pcm_rb_commit_write(pRB, framesProcessed, pFramesInExternalFormat);
5926  if (result != MA_SUCCESS) {
5927  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer.", result);
5928  break;
5929  }
5930 
5931  if (framesProcessed < framesToProcess) {
5932  break; /* Done. */
5933  }
5934  }
5935 
5936  return MA_SUCCESS;
5937 }
5938 
5939 static MA_INLINE ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB)
5940 {
5941  ma_result result;
5942  ma_uint8 playbackFramesInExternalFormat[4096];
5943  ma_uint8 silentInputFrames[4096];
5944  ma_uint32 totalFramesToReadFromClient;
5945  ma_uint32 totalFramesReadFromClient;
5946 
5947  ma_assert(pDevice != NULL);
5948  ma_assert(frameCount > 0);
5949  ma_assert(pFramesInInternalFormat != NULL);
5950  ma_assert(pRB != NULL);
5951 
5952  /*
5953  Sitting in the ring buffer should be captured data from the capture callback in external format. If there's not enough data in there for
5954  the whole frameCount frames we just use silence instead for the input data.
5955  */
5956  ma_zero_memory(silentInputFrames, sizeof(silentInputFrames));
5957 
5958  /* We need to calculate how many output frames are required to be read from the client to completely fill frameCount internal frames. */
5959  totalFramesToReadFromClient = (ma_uint32)ma_calculate_frame_count_after_src(pDevice->sampleRate, pDevice->playback.internalSampleRate, frameCount); /* ma_pcm_converter_get_required_input_frame_count(&pDevice->playback.converter, (ma_uint32)frameCount); */
5960  totalFramesReadFromClient = 0;
5961  while (totalFramesReadFromClient < totalFramesToReadFromClient && ma_device_is_started(pDevice)) {
5962  ma_uint32 framesRemainingFromClient;
5963  ma_uint32 framesToProcessFromClient;
5964  ma_uint32 inputFrameCount;
5965  void* pInputFrames;
5966 
5967  framesRemainingFromClient = (totalFramesToReadFromClient - totalFramesReadFromClient);
5968  framesToProcessFromClient = sizeof(playbackFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
5969  if (framesToProcessFromClient > framesRemainingFromClient) {
5970  framesToProcessFromClient = framesRemainingFromClient;
5971  }
5972 
5973  /* We need to grab captured samples before firing the callback. If there's not enough input samples we just pass silence. */
5974  inputFrameCount = framesToProcessFromClient;
5975  result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames);
5976  if (result == MA_SUCCESS) {
5977  if (inputFrameCount > 0) {
5978  /* Use actual input frames. */
5979  ma_device__on_data(pDevice, playbackFramesInExternalFormat, pInputFrames, inputFrameCount);
5980  } else {
5981  if (ma_pcm_rb_pointer_disance(pRB) == 0) {
5982  break; /* Underrun. */
5983  }
5984  }
5985 
5986  /* We're done with the captured samples. */
5987  result = ma_pcm_rb_commit_read(pRB, inputFrameCount, pInputFrames);
5988  if (result != MA_SUCCESS) {
5989  break; /* Don't know what to do here... Just abandon ship. */
5990  }
5991  } else {
5992  /* Use silent input frames. */
5993  inputFrameCount = ma_min(
5994  sizeof(playbackFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels),
5995  sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels)
5996  );
5997 
5998  ma_device__on_data(pDevice, playbackFramesInExternalFormat, silentInputFrames, inputFrameCount);
5999  }
6000 
6001  /* We have samples in external format so now we need to convert to internal format and output to the device. */
6002  pDevice->playback._dspFrameCount = inputFrameCount;
6003  pDevice->playback._dspFrames = (const ma_uint8*)playbackFramesInExternalFormat;
6004  ma_pcm_converter_read(&pDevice->playback.converter, pFramesInInternalFormat, inputFrameCount);
6005 
6006  totalFramesReadFromClient += inputFrameCount;
6007  pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, inputFrameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
6008  }
6009 
6010  return MA_SUCCESS;
6011 }
6012 
6013 /* A helper for changing the state of the device. */
6014 static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_uint32 newState)
6015 {
6016  ma_atomic_exchange_32(&pDevice->state, newState);
6017 }
6018 
6019 /* A helper for getting the state of the device. */
6020 static MA_INLINE ma_uint32 ma_device__get_state(ma_device* pDevice)
6021 {
6022  ma_uint32 state;
6023  ma_atomic_exchange_32(&state, pDevice->state);
6024 
6025  return state;
6026 }
6027 
6028 
6029 #ifdef MA_WIN32
6030  GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
6031  GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
6032  /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
6033  /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/
6034 #endif
6035 
6036 
6037 ma_bool32 ma_context__device_id_equal(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
6038 {
6039  ma_assert(pContext != NULL);
6040 
6041  if (pID0 == pID1) return MA_TRUE;
6042 
6043  if ((pID0 == NULL && pID1 != NULL) ||
6044  (pID0 != NULL && pID1 == NULL)) {
6045  return MA_FALSE;
6046  }
6047 
6048  if (pContext->onDeviceIDEqual) {
6049  return pContext->onDeviceIDEqual(pContext, pID0, pID1);
6050  }
6051 
6052  return MA_FALSE;
6053 }
6054 
6055 
6056 typedef struct
6057 {
6058  ma_device_type deviceType;
6059  const ma_device_id* pDeviceID;
6060  char* pName;
6061  size_t nameBufferSize;
6062  ma_bool32 foundDevice;
6063 } ma_context__try_get_device_name_by_id__enum_callback_data;
6064 
6065 ma_bool32 ma_context__try_get_device_name_by_id__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData)
6066 {
6067  ma_context__try_get_device_name_by_id__enum_callback_data* pData = (ma_context__try_get_device_name_by_id__enum_callback_data*)pUserData;
6068  ma_assert(pData != NULL);
6069 
6070  if (pData->deviceType == deviceType) {
6071  if (pContext->onDeviceIDEqual(pContext, pData->pDeviceID, &pDeviceInfo->id)) {
6072  ma_strncpy_s(pData->pName, pData->nameBufferSize, pDeviceInfo->name, (size_t)-1);
6073  pData->foundDevice = MA_TRUE;
6074  }
6075  }
6076 
6077  return !pData->foundDevice;
6078 }
6079 
6080 /*
6081 Generic function for retrieving the name of a device by it's ID.
6082 
6083 This function simply enumerates every device and then retrieves the name of the first device that has the same ID.
6084 */
6085 ma_result ma_context__try_get_device_name_by_id(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, char* pName, size_t nameBufferSize)
6086 {
6087  ma_result result;
6088  ma_context__try_get_device_name_by_id__enum_callback_data data;
6089 
6090  ma_assert(pContext != NULL);
6091  ma_assert(pName != NULL);
6092 
6093  if (pDeviceID == NULL) {
6094  return MA_NO_DEVICE;
6095  }
6096 
6097  data.deviceType = deviceType;
6098  data.pDeviceID = pDeviceID;
6099  data.pName = pName;
6100  data.nameBufferSize = nameBufferSize;
6101  data.foundDevice = MA_FALSE;
6102  result = ma_context_enumerate_devices(pContext, ma_context__try_get_device_name_by_id__enum_callback, &data);
6103  if (result != MA_SUCCESS) {
6104  return result;
6105  }
6106 
6107  if (!data.foundDevice) {
6108  return MA_NO_DEVICE;
6109  } else {
6110  return MA_SUCCESS;
6111  }
6112 }
6113 
6114 
6115 ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = better. */
6116 {
6117  ma_uint32 i;
6118  for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) {
6119  if (g_maFormatPriorities[i] == format) {
6120  return i;
6121  }
6122  }
6123 
6124  /* Getting here means the format could not be found or is equal to ma_format_unknown. */
6125  return (ma_uint32)-1;
6126 }
6127 
6128 void ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType);
6129 
6130 
6131 /*******************************************************************************
6132 
6133 Null Backend
6134 
6135 *******************************************************************************/
6136 #ifdef MA_HAS_NULL
6137 
6138 #define MA_DEVICE_OP_NONE__NULL 0
6139 #define MA_DEVICE_OP_START__NULL 1
6140 #define MA_DEVICE_OP_SUSPEND__NULL 2
6141 #define MA_DEVICE_OP_KILL__NULL 3
6142 
6143 ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData)
6144 {
6145  ma_device* pDevice = (ma_device*)pData;
6146  ma_assert(pDevice != NULL);
6147 
6148  for (;;) { /* Keep the thread alive until the device is uninitialized. */
6149  /* Wait for an operation to be requested. */
6150  ma_event_wait(&pDevice->null_device.operationEvent);
6151 
6152  /* At this point an event should have been triggered. */
6153 
6154  /* Starting the device needs to put the thread into a loop. */
6155  if (pDevice->null_device.operation == MA_DEVICE_OP_START__NULL) {
6156  ma_atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
6157 
6158  /* Reset the timer just in case. */
6159  ma_timer_init(&pDevice->null_device.timer);
6160 
6161  /* Keep looping until an operation has been requested. */
6162  while (pDevice->null_device.operation != MA_DEVICE_OP_NONE__NULL && pDevice->null_device.operation != MA_DEVICE_OP_START__NULL) {
6163  ma_sleep(10); /* Don't hog the CPU. */
6164  }
6165 
6166  /* Getting here means a suspend or kill operation has been requested. */
6167  ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS);
6168  ma_event_signal(&pDevice->null_device.operationCompletionEvent);
6169  continue;
6170  }
6171 
6172  /* Suspending the device means we need to stop the timer and just continue the loop. */
6173  if (pDevice->null_device.operation == MA_DEVICE_OP_SUSPEND__NULL) {
6174  ma_atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
6175 
6176  /* We need to add the current run time to the prior run time, then reset the timer. */
6177  pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
6178  ma_timer_init(&pDevice->null_device.timer);
6179 
6180  /* We're done. */
6181  ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS);
6182  ma_event_signal(&pDevice->null_device.operationCompletionEvent);
6183  continue;
6184  }
6185 
6186  /* Killing the device means we need to get out of this loop so that this thread can terminate. */
6187  if (pDevice->null_device.operation == MA_DEVICE_OP_KILL__NULL) {
6188  ma_atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);
6189  ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_SUCCESS);
6190  ma_event_signal(&pDevice->null_device.operationCompletionEvent);
6191  break;
6192  }
6193 
6194  /* Getting a signal on a "none" operation probably means an error. Return invalid operation. */
6195  if (pDevice->null_device.operation == MA_DEVICE_OP_NONE__NULL) {
6196  ma_assert(MA_FALSE); /* <-- Trigger this in debug mode to ensure developers are aware they're doing something wrong (or there's a bug in a miniaudio). */
6197  ma_atomic_exchange_32(&pDevice->null_device.operationResult, MA_INVALID_OPERATION);
6198  ma_event_signal(&pDevice->null_device.operationCompletionEvent);
6199  continue; /* Continue the loop. Don't terminate. */
6200  }
6201  }
6202 
6203  return (ma_thread_result)0;
6204 }
6205 
6206 ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation)
6207 {
6208  ma_atomic_exchange_32(&pDevice->null_device.operation, operation);
6209  if (!ma_event_signal(&pDevice->null_device.operationEvent)) {
6210  return MA_ERROR;
6211  }
6212 
6213  if (!ma_event_wait(&pDevice->null_device.operationCompletionEvent)) {
6214  return MA_ERROR;
6215  }
6216 
6217  return pDevice->null_device.operationResult;
6218 }
6219 
6220 ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
6221 {
6222  ma_uint32 internalSampleRate;
6223  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
6224  internalSampleRate = pDevice->capture.internalSampleRate;
6225  } else {
6226  internalSampleRate = pDevice->playback.internalSampleRate;
6227  }
6228 
6229 
6230  return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
6231 }
6232 
6233 ma_bool32 ma_context_is_device_id_equal__null(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
6234 {
6235  ma_assert(pContext != NULL);
6236  ma_assert(pID0 != NULL);
6237  ma_assert(pID1 != NULL);
6238  (void)pContext;
6239 
6240  return pID0->nullbackend == pID1->nullbackend;
6241 }
6242 
6243 ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
6244 {
6245  ma_bool32 cbResult = MA_TRUE;
6246 
6247  ma_assert(pContext != NULL);
6248  ma_assert(callback != NULL);
6249 
6250  /* Playback. */
6251  if (cbResult) {
6252  ma_device_info deviceInfo;
6253  ma_zero_object(&deviceInfo);
6254  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1);
6255  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
6256  }
6257 
6258  /* Capture. */
6259  if (cbResult) {
6260  ma_device_info deviceInfo;
6261  ma_zero_object(&deviceInfo);
6262  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1);
6263  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
6264  }
6265 
6266  return MA_SUCCESS;
6267 }
6268 
6269 ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
6270 {
6271  ma_uint32 iFormat;
6272 
6273  ma_assert(pContext != NULL);
6274 
6275  if (pDeviceID != NULL && pDeviceID->nullbackend != 0) {
6276  return MA_NO_DEVICE; /* Don't know the device. */
6277  }
6278 
6279  /* Name / Description */
6280  if (deviceType == ma_device_type_playback) {
6281  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1);
6282  } else {
6283  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1);
6284  }
6285 
6286  /* Support everything on the null backend. */
6287  pDeviceInfo->formatCount = ma_format_count - 1; /* Minus one because we don't want to include ma_format_unknown. */
6288  for (iFormat = 0; iFormat < pDeviceInfo->formatCount; ++iFormat) {
6289  pDeviceInfo->formats[iFormat] = (ma_format)(iFormat + 1); /* +1 to skip over ma_format_unknown. */
6290  }
6291 
6292  pDeviceInfo->minChannels = 1;
6293  pDeviceInfo->maxChannels = MA_MAX_CHANNELS;
6294  pDeviceInfo->minSampleRate = MA_SAMPLE_RATE_8000;
6295  pDeviceInfo->maxSampleRate = MA_SAMPLE_RATE_384000;
6296 
6297  (void)pContext;
6298  (void)shareMode;
6299  return MA_SUCCESS;
6300 }
6301 
6302 
6303 void ma_device_uninit__null(ma_device* pDevice)
6304 {
6305  ma_assert(pDevice != NULL);
6306 
6307  /* Keep it clean and wait for the device thread to finish before returning. */
6308  ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL);
6309 
6310  /* At this point the loop in the device thread is as good as terminated so we can uninitialize our events. */
6311  ma_event_uninit(&pDevice->null_device.operationCompletionEvent);
6312  ma_event_uninit(&pDevice->null_device.operationEvent);
6313 }
6314 
6315 ma_result ma_device_init__null(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
6316 {
6317  ma_result result;
6318  ma_uint32 bufferSizeInFrames;
6319 
6320  ma_assert(pDevice != NULL);
6321 
6322  ma_zero_object(&pDevice->null_device);
6323 
6324  if (pConfig->deviceType == ma_device_type_loopback) {
6326  }
6327 
6328  bufferSizeInFrames = pConfig->bufferSizeInFrames;
6329  if (bufferSizeInFrames == 0) {
6331  }
6332 
6333  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
6334  ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "NULL Capture Device", (size_t)-1);
6335  pDevice->capture.internalFormat = pConfig->capture.format;
6336  pDevice->capture.internalChannels = pConfig->capture.channels;
6337  ma_channel_map_copy(pDevice->capture.internalChannelMap, pConfig->capture.channelMap, pConfig->capture.channels);
6338  pDevice->capture.internalBufferSizeInFrames = bufferSizeInFrames;
6339  pDevice->capture.internalPeriods = pConfig->periods;
6340  }
6341  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
6342  ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "NULL Playback Device", (size_t)-1);
6343  pDevice->playback.internalFormat = pConfig->playback.format;
6344  pDevice->playback.internalChannels = pConfig->playback.channels;
6345  ma_channel_map_copy(pDevice->playback.internalChannelMap, pConfig->playback.channelMap, pConfig->playback.channels);
6346  pDevice->playback.internalBufferSizeInFrames = bufferSizeInFrames;
6347  pDevice->playback.internalPeriods = pConfig->periods;
6348  }
6349 
6350  /*
6351  In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the
6352  first period is "written" to it, and then stopped in ma_device_stop__null().
6353  */
6354  result = ma_event_init(pContext, &pDevice->null_device.operationEvent);
6355  if (result != MA_SUCCESS) {
6356  return result;
6357  }
6358 
6359  result = ma_event_init(pContext, &pDevice->null_device.operationCompletionEvent);
6360  if (result != MA_SUCCESS) {
6361  return result;
6362  }
6363 
6364  result = ma_thread_create(pContext, &pDevice->thread, ma_device_thread__null, pDevice);
6365  if (result != MA_SUCCESS) {
6366  return result;
6367  }
6368 
6369  return MA_SUCCESS;
6370 }
6371 
6372 ma_result ma_device_start__null(ma_device* pDevice)
6373 {
6374  ma_assert(pDevice != NULL);
6375 
6376  ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL);
6377 
6378  ma_atomic_exchange_32(&pDevice->null_device.isStarted, MA_TRUE);
6379  return MA_SUCCESS;
6380 }
6381 
6382 ma_result ma_device_stop__null(ma_device* pDevice)
6383 {
6384  ma_assert(pDevice != NULL);
6385 
6386  ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL);
6387 
6388  ma_atomic_exchange_32(&pDevice->null_device.isStarted, MA_FALSE);
6389  return MA_SUCCESS;
6390 }
6391 
6392 ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
6393 {
6395  ma_uint32 totalPCMFramesProcessed;
6396  ma_bool32 wasStartedOnEntry;
6397 
6398  if (pFramesWritten != NULL) {
6399  *pFramesWritten = 0;
6400  }
6401 
6402  wasStartedOnEntry = pDevice->null_device.isStarted;
6403 
6404  /* Keep going until everything has been read. */
6405  totalPCMFramesProcessed = 0;
6406  while (totalPCMFramesProcessed < frameCount) {
6407  ma_uint64 targetFrame;
6408 
6409  /* If there are any frames remaining in the current period, consume those first. */
6410  if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) {
6411  ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
6412  ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback;
6413  if (framesToProcess > framesRemaining) {
6414  framesToProcess = framesRemaining;
6415  }
6416 
6417  /* We don't actually do anything with pPCMFrames, so just mark it as unused to prevent a warning. */
6418  (void)pPCMFrames;
6419 
6420  pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess;
6421  totalPCMFramesProcessed += framesToProcess;
6422  }
6423 
6424  /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */
6425  if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) {
6426  pDevice->null_device.currentPeriodFramesRemainingPlayback = 0;
6427 
6428  if (!pDevice->null_device.isStarted && !wasStartedOnEntry) {
6429  result = ma_device_start__null(pDevice);
6430  if (result != MA_SUCCESS) {
6431  break;
6432  }
6433  }
6434  }
6435 
6436  /* If we've consumed the whole buffer we can return now. */
6437  ma_assert(totalPCMFramesProcessed <= frameCount);
6438  if (totalPCMFramesProcessed == frameCount) {
6439  break;
6440  }
6441 
6442  /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
6443  targetFrame = pDevice->null_device.lastProcessedFramePlayback;
6444  for (;;) {
6445  ma_uint64 currentFrame;
6446 
6447  /* Stop waiting if the device has been stopped. */
6448  if (!pDevice->null_device.isStarted) {
6449  break;
6450  }
6451 
6452  currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
6453  if (currentFrame >= targetFrame) {
6454  break;
6455  }
6456 
6457  /* Getting here means we haven't yet reached the target sample, so continue waiting. */
6458  ma_sleep(10);
6459  }
6460 
6461  pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods;
6462  pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods;
6463  }
6464 
6465  if (pFramesWritten != NULL) {
6466  *pFramesWritten = totalPCMFramesProcessed;
6467  }
6468 
6469  return result;
6470 }
6471 
6472 ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
6473 {
6475  ma_uint32 totalPCMFramesProcessed;
6476 
6477  if (pFramesRead != NULL) {
6478  *pFramesRead = 0;
6479  }
6480 
6481  /* Keep going until everything has been read. */
6482  totalPCMFramesProcessed = 0;
6483  while (totalPCMFramesProcessed < frameCount) {
6484  ma_uint64 targetFrame;
6485 
6486  /* If there are any frames remaining in the current period, consume those first. */
6487  if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) {
6488  ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
6489  ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
6490  ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture;
6491  if (framesToProcess > framesRemaining) {
6492  framesToProcess = framesRemaining;
6493  }
6494 
6495  /* We need to ensured the output buffer is zeroed. */
6496  ma_zero_memory(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf);
6497 
6498  pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess;
6499  totalPCMFramesProcessed += framesToProcess;
6500  }
6501 
6502  /* If we've consumed the current period we'll need to mark it as such an ensure the device is started if it's not already. */
6503  if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) {
6504  pDevice->null_device.currentPeriodFramesRemainingCapture = 0;
6505  }
6506 
6507  /* If we've consumed the whole buffer we can return now. */
6508  ma_assert(totalPCMFramesProcessed <= frameCount);
6509  if (totalPCMFramesProcessed == frameCount) {
6510  break;
6511  }
6512 
6513  /* Getting here means we've still got more frames to consume, we but need to wait for it to become available. */
6514  targetFrame = pDevice->null_device.lastProcessedFrameCapture + (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods);
6515  for (;;) {
6516  ma_uint64 currentFrame;
6517 
6518  /* Stop waiting if the device has been stopped. */
6519  if (!pDevice->null_device.isStarted) {
6520  break;
6521  }
6522 
6523  currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
6524  if (currentFrame >= targetFrame) {
6525  break;
6526  }
6527 
6528  /* Getting here means we haven't yet reached the target sample, so continue waiting. */
6529  ma_sleep(10);
6530  }
6531 
6532  pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods;
6533  pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods;
6534  }
6535 
6536  if (pFramesRead != NULL) {
6537  *pFramesRead = totalPCMFramesProcessed;
6538  }
6539 
6540  return result;
6541 }
6542 
6543 ma_result ma_device_main_loop__null(ma_device* pDevice)
6544 {
6546  ma_bool32 exitLoop = MA_FALSE;
6547 
6548  ma_assert(pDevice != NULL);
6549 
6550  /* The capture device needs to be started immediately. */
6551  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
6552  result = ma_device_start__null(pDevice);
6553  if (result != MA_SUCCESS) {
6554  return result;
6555  }
6556  }
6557 
6558  while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
6559  switch (pDevice->type)
6560  {
6561  case ma_device_type_duplex:
6562  {
6563  /* The process is: device_read -> convert -> callback -> convert -> device_write */
6564  ma_uint8 capturedDeviceData[8192];
6565  ma_uint8 playbackDeviceData[8192];
6566  ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
6567  ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
6568 
6569  ma_uint32 totalFramesProcessed = 0;
6570  ma_uint32 periodSizeInFrames = ma_min(pDevice->capture.internalBufferSizeInFrames/pDevice->capture.internalPeriods, pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods);
6571 
6572  while (totalFramesProcessed < periodSizeInFrames) {
6573  ma_uint32 framesRemaining = periodSizeInFrames - totalFramesProcessed;
6574  ma_uint32 framesProcessed;
6575  ma_uint32 framesToProcess = framesRemaining;
6576  if (framesToProcess > capturedDeviceDataCapInFrames) {
6577  framesToProcess = capturedDeviceDataCapInFrames;
6578  }
6579 
6580  result = ma_device_read__null(pDevice, capturedDeviceData, framesToProcess, &framesProcessed);
6581  if (result != MA_SUCCESS) {
6582  exitLoop = MA_TRUE;
6583  break;
6584  }
6585 
6586  pDevice->capture._dspFrameCount = framesToProcess;
6587  pDevice->capture._dspFrames = capturedDeviceData;
6588 
6589  for (;;) {
6590  ma_uint8 capturedData[8192];
6591  ma_uint8 playbackData[8192];
6592  ma_uint32 capturedDataCapInFrames = sizeof(capturedData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
6593  ma_uint32 playbackDataCapInFrames = sizeof(playbackData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
6594 
6595  ma_uint32 capturedFramesToTryProcessing = ma_min(capturedDataCapInFrames, playbackDataCapInFrames);
6596  ma_uint32 capturedFramesToProcess = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, capturedData, capturedFramesToTryProcessing);
6597  if (capturedFramesToProcess == 0) {
6598  break; /* Don't fire the data callback with zero frames. */
6599  }
6600 
6601  ma_device__on_data(pDevice, playbackData, capturedData, capturedFramesToProcess);
6602 
6603  /* At this point the playbackData buffer should be holding data that needs to be written to the device. */
6604  pDevice->playback._dspFrameCount = capturedFramesToProcess;
6605  pDevice->playback._dspFrames = playbackData;
6606  for (;;) {
6607  ma_uint32 playbackDeviceFramesCount = (ma_uint32)ma_pcm_converter_read(&pDevice->playback.converter, playbackDeviceData, playbackDeviceDataCapInFrames);
6608  if (playbackDeviceFramesCount == 0) {
6609  break;
6610  }
6611 
6612  result = ma_device_write__null(pDevice, playbackDeviceData, playbackDeviceFramesCount, NULL);
6613  if (result != MA_SUCCESS) {
6614  exitLoop = MA_TRUE;
6615  break;
6616  }
6617 
6618  if (playbackDeviceFramesCount < playbackDeviceDataCapInFrames) {
6619  break;
6620  }
6621  }
6622 
6623  if (capturedFramesToProcess < capturedFramesToTryProcessing) {
6624  break;
6625  }
6626 
6627  /* In case an error happened from ma_device_write2__alsa()... */
6628  if (result != MA_SUCCESS) {
6629  exitLoop = MA_TRUE;
6630  break;
6631  }
6632  }
6633 
6634  totalFramesProcessed += framesProcessed;
6635  }
6636  } break;
6637 
6639  {
6640  /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
6641  ma_uint8 intermediaryBuffer[8192];
6642  ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
6643  ma_uint32 periodSizeInFrames = pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods;
6644  ma_uint32 framesReadThisPeriod = 0;
6645  while (framesReadThisPeriod < periodSizeInFrames) {
6646  ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
6647  ma_uint32 framesProcessed;
6648  ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
6649  if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
6650  framesToReadThisIteration = intermediaryBufferSizeInFrames;
6651  }
6652 
6653  result = ma_device_read__null(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
6654  if (result != MA_SUCCESS) {
6655  exitLoop = MA_TRUE;
6656  break;
6657  }
6658 
6659  ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
6660 
6661  framesReadThisPeriod += framesProcessed;
6662  }
6663  } break;
6664 
6666  {
6667  /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
6668  ma_uint8 intermediaryBuffer[8192];
6669  ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
6670  ma_uint32 periodSizeInFrames = pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods;
6671  ma_uint32 framesWrittenThisPeriod = 0;
6672  while (framesWrittenThisPeriod < periodSizeInFrames) {
6673  ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
6674  ma_uint32 framesProcessed;
6675  ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
6676  if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
6677  framesToWriteThisIteration = intermediaryBufferSizeInFrames;
6678  }
6679 
6680  ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
6681 
6682  result = ma_device_write__null(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
6683  if (result != MA_SUCCESS) {
6684  exitLoop = MA_TRUE;
6685  break;
6686  }
6687 
6688  framesWrittenThisPeriod += framesProcessed;
6689  }
6690  } break;
6691 
6692  /* To silence a warning. Will never hit this. */
6694  default: break;
6695  }
6696  }
6697 
6698 
6699  /* Here is where the device is started. */
6700  ma_device_stop__null(pDevice);
6701 
6702  return result;
6703 }
6704 
6705 ma_result ma_context_uninit__null(ma_context* pContext)
6706 {
6707  ma_assert(pContext != NULL);
6708  ma_assert(pContext->backend == ma_backend_null);
6709 
6710  (void)pContext;
6711  return MA_SUCCESS;
6712 }
6713 
6714 ma_result ma_context_init__null(const ma_context_config* pConfig, ma_context* pContext)
6715 {
6716  ma_assert(pContext != NULL);
6717 
6718  (void)pConfig;
6719 
6720  pContext->onUninit = ma_context_uninit__null;
6721  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__null;
6722  pContext->onEnumDevices = ma_context_enumerate_devices__null;
6723  pContext->onGetDeviceInfo = ma_context_get_device_info__null;
6724  pContext->onDeviceInit = ma_device_init__null;
6725  pContext->onDeviceUninit = ma_device_uninit__null;
6726  pContext->onDeviceStart = NULL; /* Not required for synchronous backends. */
6727  pContext->onDeviceStop = NULL; /* Not required for synchronous backends. */
6728  pContext->onDeviceMainLoop = ma_device_main_loop__null;
6729 
6730  /* The null backend always works. */
6731  return MA_SUCCESS;
6732 }
6733 #endif
6734 
6735 
6736 /*******************************************************************************
6737 
6738 WIN32 COMMON
6739 
6740 *******************************************************************************/
6741 #if defined(MA_WIN32)
6742 #if defined(MA_WIN32_DESKTOP)
6743  #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit)
6744  #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)()
6745  #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv)
6746  #define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv)
6747  #define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar)
6748 #else
6749  #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit)
6750  #define ma_CoUninitialize(pContext) CoUninitialize()
6751  #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv)
6752  #define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv)
6753  #define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar)
6754 #endif
6755 
6756 #if !defined(MAXULONG_PTR)
6757 typedef size_t DWORD_PTR;
6758 #endif
6759 
6760 #if !defined(WAVE_FORMAT_44M08)
6761 #define WAVE_FORMAT_44M08 0x00000100
6762 #define WAVE_FORMAT_44S08 0x00000200
6763 #define WAVE_FORMAT_44M16 0x00000400
6764 #define WAVE_FORMAT_44S16 0x00000800
6765 #define WAVE_FORMAT_48M08 0x00001000
6766 #define WAVE_FORMAT_48S08 0x00002000
6767 #define WAVE_FORMAT_48M16 0x00004000
6768 #define WAVE_FORMAT_48S16 0x00008000
6769 #define WAVE_FORMAT_96M08 0x00010000
6770 #define WAVE_FORMAT_96S08 0x00020000
6771 #define WAVE_FORMAT_96M16 0x00040000
6772 #define WAVE_FORMAT_96S16 0x00080000
6773 #endif
6774 
6775 #ifndef SPEAKER_FRONT_LEFT
6776 #define SPEAKER_FRONT_LEFT 0x1
6777 #define SPEAKER_FRONT_RIGHT 0x2
6778 #define SPEAKER_FRONT_CENTER 0x4
6779 #define SPEAKER_LOW_FREQUENCY 0x8
6780 #define SPEAKER_BACK_LEFT 0x10
6781 #define SPEAKER_BACK_RIGHT 0x20
6782 #define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
6783 #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
6784 #define SPEAKER_BACK_CENTER 0x100
6785 #define SPEAKER_SIDE_LEFT 0x200
6786 #define SPEAKER_SIDE_RIGHT 0x400
6787 #define SPEAKER_TOP_CENTER 0x800
6788 #define SPEAKER_TOP_FRONT_LEFT 0x1000
6789 #define SPEAKER_TOP_FRONT_CENTER 0x2000
6790 #define SPEAKER_TOP_FRONT_RIGHT 0x4000
6791 #define SPEAKER_TOP_BACK_LEFT 0x8000
6792 #define SPEAKER_TOP_BACK_CENTER 0x10000
6793 #define SPEAKER_TOP_BACK_RIGHT 0x20000
6794 #endif
6795 
6796 /*
6797 The SDK that comes with old versions of MSVC (VC6, for example) does not appear to define WAVEFORMATEXTENSIBLE. We
6798 define our own implementation in this case.
6799 */
6800 #if (defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_)) || defined(__DMC__)
6801 typedef struct
6802 {
6803  WAVEFORMATEX Format;
6804  union
6805  {
6806  WORD wValidBitsPerSample;
6807  WORD wSamplesPerBlock;
6808  WORD wReserved;
6809  } Samples;
6810  DWORD dwChannelMask;
6811  GUID SubFormat;
6812 } WAVEFORMATEXTENSIBLE;
6813 #endif
6814 
6815 #ifndef WAVE_FORMAT_EXTENSIBLE
6816 #define WAVE_FORMAT_EXTENSIBLE 0xFFFE
6817 #endif
6818 
6819 #ifndef WAVE_FORMAT_IEEE_FLOAT
6820 #define WAVE_FORMAT_IEEE_FLOAT 0x0003
6821 #endif
6822 
6823 GUID MA_GUID_NULL = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
6824 
6825 /* Converts an individual Win32-style channel identifier (SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
6826 ma_uint8 ma_channel_id_to_ma__win32(DWORD id)
6827 {
6828  switch (id)
6829  {
6830  case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
6831  case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
6832  case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
6833  case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
6834  case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
6835  case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
6836  case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
6837  case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
6838  case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
6839  case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
6840  case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
6841  case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
6842  case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
6843  case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
6844  case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
6845  case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
6846  case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
6847  case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
6848  default: return 0;
6849  }
6850 }
6851 
6852 /* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to Win32-style. */
6853 DWORD ma_channel_id_to_win32(DWORD id)
6854 {
6855  switch (id)
6856  {
6857  case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER;
6858  case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT;
6859  case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT;
6860  case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER;
6861  case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY;
6862  case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT;
6863  case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT;
6864  case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER;
6865  case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER;
6866  case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER;
6867  case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT;
6868  case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT;
6869  case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER;
6870  case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT;
6871  case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER;
6872  case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT;
6873  case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT;
6874  case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER;
6875  case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT;
6876  default: return 0;
6877  }
6878 }
6879 
6880 /* Converts a channel mapping to a Win32-style channel mask. */
6881 DWORD ma_channel_map_to_channel_mask__win32(const ma_channel channelMap[MA_MAX_CHANNELS], ma_uint32 channels)
6882 {
6883  DWORD dwChannelMask = 0;
6884  ma_uint32 iChannel;
6885 
6886  for (iChannel = 0; iChannel < channels; ++iChannel) {
6887  dwChannelMask |= ma_channel_id_to_win32(channelMap[iChannel]);
6888  }
6889 
6890  return dwChannelMask;
6891 }
6892 
6893 /* Converts a Win32-style channel mask to a miniaudio channel map. */
6894 void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
6895 {
6896  if (channels == 1 && dwChannelMask == 0) {
6897  channelMap[0] = MA_CHANNEL_MONO;
6898  } else if (channels == 2 && dwChannelMask == 0) {
6899  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
6900  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
6901  } else {
6902  if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) {
6903  channelMap[0] = MA_CHANNEL_MONO;
6904  } else {
6905  /* Just iterate over each bit. */
6906  ma_uint32 iChannel = 0;
6907  ma_uint32 iBit;
6908 
6909  for (iBit = 0; iBit < 32; ++iBit) {
6910  DWORD bitValue = (dwChannelMask & (1UL << iBit));
6911  if (bitValue != 0) {
6912  /* The bit is set. */
6913  channelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue);
6914  iChannel += 1;
6915  }
6916  }
6917  }
6918  }
6919 }
6920 
6921 #ifdef __cplusplus
6922 ma_bool32 ma_is_guid_equal(const void* a, const void* b)
6923 {
6924  return IsEqualGUID(*(const GUID*)a, *(const GUID*)b);
6925 }
6926 #else
6927 #define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b)
6928 #endif
6929 
6930 ma_format ma_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF)
6931 {
6932  ma_assert(pWF != NULL);
6933 
6934  if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
6935  const WAVEFORMATEXTENSIBLE* pWFEX = (const WAVEFORMATEXTENSIBLE*)pWF;
6936  if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) {
6937  if (pWFEX->Samples.wValidBitsPerSample == 32) {
6938  return ma_format_s32;
6939  }
6940  if (pWFEX->Samples.wValidBitsPerSample == 24) {
6941  if (pWFEX->Format.wBitsPerSample == 32) {
6942  /*return ma_format_s24_32;*/
6943  }
6944  if (pWFEX->Format.wBitsPerSample == 24) {
6945  return ma_format_s24;
6946  }
6947  }
6948  if (pWFEX->Samples.wValidBitsPerSample == 16) {
6949  return ma_format_s16;
6950  }
6951  if (pWFEX->Samples.wValidBitsPerSample == 8) {
6952  return ma_format_u8;
6953  }
6954  }
6955  if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
6956  if (pWFEX->Samples.wValidBitsPerSample == 32) {
6957  return ma_format_f32;
6958  }
6959  /*
6960  if (pWFEX->Samples.wValidBitsPerSample == 64) {
6961  return ma_format_f64;
6962  }
6963  */
6964  }
6965  } else {
6966  if (pWF->wFormatTag == WAVE_FORMAT_PCM) {
6967  if (pWF->wBitsPerSample == 32) {
6968  return ma_format_s32;
6969  }
6970  if (pWF->wBitsPerSample == 24) {
6971  return ma_format_s24;
6972  }
6973  if (pWF->wBitsPerSample == 16) {
6974  return ma_format_s16;
6975  }
6976  if (pWF->wBitsPerSample == 8) {
6977  return ma_format_u8;
6978  }
6979  }
6980  if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
6981  if (pWF->wBitsPerSample == 32) {
6982  return ma_format_f32;
6983  }
6984  if (pWF->wBitsPerSample == 64) {
6985  /*return ma_format_f64;*/
6986  }
6987  }
6988  }
6989 
6990  return ma_format_unknown;
6991 }
6992 #endif
6993 
6994 
6995 /*******************************************************************************
6996 
6997 WASAPI Backend
6998 
6999 *******************************************************************************/
7000 #ifdef MA_HAS_WASAPI
7001 #if 0
7002 #if defined(_MSC_VER)
7003  #pragma warning(push)
7004  #pragma warning(disable:4091) /* 'typedef ': ignored on left of '' when no variable is declared */
7005 #endif
7006 #include <audioclient.h>
7007 #include <mmdeviceapi.h>
7008 #if defined(_MSC_VER)
7009  #pragma warning(pop)
7010 #endif
7011 #endif /* 0 */
7012 
7013 
7014 
7015 
7016 /* Some compilers don't define VerifyVersionInfoW. Need to write this ourselves. */
7017 #define MA_WIN32_WINNT_VISTA 0x0600
7018 #define MA_VER_MINORVERSION 0x01
7019 #define MA_VER_MAJORVERSION 0x02
7020 #define MA_VER_SERVICEPACKMAJOR 0x20
7021 #define MA_VER_GREATER_EQUAL 0x03
7022 
7023 typedef struct {
7024  DWORD dwOSVersionInfoSize;
7025  DWORD dwMajorVersion;
7026  DWORD dwMinorVersion;
7027  DWORD dwBuildNumber;
7028  DWORD dwPlatformId;
7029  WCHAR szCSDVersion[128];
7030  WORD wServicePackMajor;
7031  WORD wServicePackMinor;
7032  WORD wSuiteMask;
7033  BYTE wProductType;
7034  BYTE wReserved;
7035 } ma_OSVERSIONINFOEXW;
7036 
7037 typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
7038 typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask);
7039 
7040 
7041 #ifndef PROPERTYKEY_DEFINED
7042 #define PROPERTYKEY_DEFINED
7043 typedef struct
7044 {
7045  GUID fmtid;
7046  DWORD pid;
7047 } PROPERTYKEY;
7048 #endif
7049 
7050 /* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */
7051 static MA_INLINE void ma_PropVariantInit(PROPVARIANT* pProp)
7052 {
7053  ma_zero_object(pProp);
7054 }
7055 
7056 
7057 const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14};
7058 const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0};
7059 
7060 const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}}; /* 00000000-0000-0000-C000-000000000046 */
7061 const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}}; /* 94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90 */
7062 
7063 const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}}; /* 1CB9AD4C-DBFA-4C32-B178-C2F568A703B2 = __uuidof(IAudioClient) */
7064 const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}}; /* 726778CD-F60A-4EDA-82DE-E47610CD78AA = __uuidof(IAudioClient2) */
7065 const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}}; /* 7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42 = __uuidof(IAudioClient3) */
7066 const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}}; /* F294ACFC-3146-4483-A7BF-ADDCA7C260E2 = __uuidof(IAudioRenderClient) */
7067 const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}}; /* C8ADBD64-E71E-48A0-A4DE-185C395CD317 = __uuidof(IAudioCaptureClient) */
7068 const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}}; /* 7991EEC9-7E89-4D85-8390-6C703CEC60C0 = __uuidof(IMMNotificationClient) */
7069 #ifndef MA_WIN32_DESKTOP
7070 const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}}; /* E6327CAD-DCEC-4949-AE8A-991E976A79D2 */
7071 const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}}; /* 2EEF81BE-33FA-4800-9670-1CD474972C3F */
7072 const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */
7073 #endif
7074 
7075 const IID MA_CLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */
7076 const IID MA_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */
7077 #ifdef __cplusplus
7078 #define MA_CLSID_MMDeviceEnumerator MA_CLSID_MMDeviceEnumerator_Instance
7079 #define MA_IID_IMMDeviceEnumerator MA_IID_IMMDeviceEnumerator_Instance
7080 #else
7081 #define MA_CLSID_MMDeviceEnumerator &MA_CLSID_MMDeviceEnumerator_Instance
7082 #define MA_IID_IMMDeviceEnumerator &MA_IID_IMMDeviceEnumerator_Instance
7083 #endif
7084 
7085 typedef struct ma_IUnknown ma_IUnknown;
7086 #ifdef MA_WIN32_DESKTOP
7087 #define MA_MM_DEVICE_STATE_ACTIVE 1
7088 #define MA_MM_DEVICE_STATE_DISABLED 2
7089 #define MA_MM_DEVICE_STATE_NOTPRESENT 4
7090 #define MA_MM_DEVICE_STATE_UNPLUGGED 8
7091 
7092 typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator;
7093 typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection;
7094 typedef struct ma_IMMDevice ma_IMMDevice;
7095 #else
7096 typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler;
7097 typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation;
7098 #endif
7099 typedef struct ma_IPropertyStore ma_IPropertyStore;
7100 typedef struct ma_IAudioClient ma_IAudioClient;
7101 typedef struct ma_IAudioClient2 ma_IAudioClient2;
7102 typedef struct ma_IAudioClient3 ma_IAudioClient3;
7103 typedef struct ma_IAudioRenderClient ma_IAudioRenderClient;
7104 typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient;
7105 
7106 typedef ma_int64 MA_REFERENCE_TIME;
7107 
7108 #define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000
7109 #define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000
7110 #define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000
7111 #define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000
7112 #define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
7113 #define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
7114 #define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
7115 #define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000
7116 #define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000
7117 #define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000
7118 
7119 /* We only care about a few error codes. */
7120 #define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD (-2004287456)
7121 #define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED (-2004287463)
7122 #define MA_AUDCLNT_S_BUFFER_EMPTY (143196161)
7123 #define MA_AUDCLNT_E_DEVICE_IN_USE (-2004287478)
7124 
7125 /* Buffer flags. */
7126 #define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1
7127 #define MA_AUDCLNT_BUFFERFLAGS_SILENT 2
7128 #define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4
7129 
7130 typedef enum
7131 {
7132  ma_eRender = 0,
7133  ma_eCapture = 1,
7134  ma_eAll = 2
7135 } ma_EDataFlow;
7136 
7137 typedef enum
7138 {
7139  ma_eConsole = 0,
7140  ma_eMultimedia = 1,
7141  ma_eCommunications = 2
7142 } ma_ERole;
7143 
7144 typedef enum
7145 {
7146  MA_AUDCLNT_SHAREMODE_SHARED,
7147  MA_AUDCLNT_SHAREMODE_EXCLUSIVE
7148 } MA_AUDCLNT_SHAREMODE;
7149 
7150 typedef enum
7151 {
7152  MA_AudioCategory_Other = 0 /* <-- miniaudio is only caring about Other. */
7153 } MA_AUDIO_STREAM_CATEGORY;
7154 
7155 typedef struct
7156 {
7157  UINT32 cbSize;
7158  BOOL bIsOffload;
7159  MA_AUDIO_STREAM_CATEGORY eCategory;
7160 } ma_AudioClientProperties;
7161 
7162 /* IUnknown */
7163 typedef struct
7164 {
7165  /* IUnknown */
7166  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject);
7167  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis);
7168  ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis);
7169 } ma_IUnknownVtbl;
7170 struct ma_IUnknown
7171 {
7172  ma_IUnknownVtbl* lpVtbl;
7173 };
7174 HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
7175 ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); }
7176 ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); }
7177 
7178 #ifdef MA_WIN32_DESKTOP
7179  /* IMMNotificationClient */
7180  typedef struct
7181  {
7182  /* IUnknown */
7183  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject);
7184  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis);
7185  ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis);
7186 
7187  /* IMMNotificationClient */
7188  HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState);
7189  HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID);
7190  HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID);
7191  HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID);
7192  HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key);
7193  } ma_IMMNotificationClientVtbl;
7194 
7195  /* IMMDeviceEnumerator */
7196  typedef struct
7197  {
7198  /* IUnknown */
7199  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject);
7200  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis);
7201  ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis);
7202 
7203  /* IMMDeviceEnumerator */
7204  HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices);
7205  HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint);
7206  HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice);
7207  HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
7208  HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
7209  } ma_IMMDeviceEnumeratorVtbl;
7210  struct ma_IMMDeviceEnumerator
7211  {
7212  ma_IMMDeviceEnumeratorVtbl* lpVtbl;
7213  };
7214  HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
7215  ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); }
7216  ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); }
7217  HRESULT ma_IMMDeviceEnumerator_EnumAudioEndpoints(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); }
7218  HRESULT ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); }
7219  HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); }
7220  HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); }
7221  HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); }
7222 
7223 
7224  /* IMMDeviceCollection */
7225  typedef struct
7226  {
7227  /* IUnknown */
7228  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject);
7229  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis);
7230  ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis);
7231 
7232  /* IMMDeviceCollection */
7233  HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices);
7234  HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice);
7235  } ma_IMMDeviceCollectionVtbl;
7236  struct ma_IMMDeviceCollection
7237  {
7238  ma_IMMDeviceCollectionVtbl* lpVtbl;
7239  };
7240  HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
7241  ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); }
7242  ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); }
7243  HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); }
7244  HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); }
7245 
7246 
7247  /* IMMDevice */
7248  typedef struct
7249  {
7250  /* IUnknown */
7251  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject);
7252  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis);
7253  ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis);
7254 
7255  /* IMMDevice */
7256  HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface);
7257  HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties);
7258  HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, LPWSTR *pID);
7259  HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState);
7260  } ma_IMMDeviceVtbl;
7261  struct ma_IMMDevice
7262  {
7263  ma_IMMDeviceVtbl* lpVtbl;
7264  };
7265  HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
7266  ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); }
7267  ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); }
7268  HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); }
7269  HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); }
7270  HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, LPWSTR *pID) { return pThis->lpVtbl->GetId(pThis, pID); }
7271  HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); }
7272 #else
7273  /* IActivateAudioInterfaceAsyncOperation */
7274  typedef struct
7275  {
7276  /* IUnknown */
7277  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject);
7278  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
7279  ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
7280 
7281  /* IActivateAudioInterfaceAsyncOperation */
7282  HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface);
7283  } ma_IActivateAudioInterfaceAsyncOperationVtbl;
7284  struct ma_IActivateAudioInterfaceAsyncOperation
7285  {
7286  ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl;
7287  };
7288  HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
7289  ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); }
7290  ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); }
7291  HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); }
7292 #endif
7293 
7294 /* IPropertyStore */
7295 typedef struct
7296 {
7297  /* IUnknown */
7298  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject);
7299  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis);
7300  ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis);
7301 
7302  /* IPropertyStore */
7303  HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount);
7304  HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey);
7305  HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar);
7306  HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar);
7307  HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis);
7308 } ma_IPropertyStoreVtbl;
7309 struct ma_IPropertyStore
7310 {
7311  ma_IPropertyStoreVtbl* lpVtbl;
7312 };
7313 HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
7314 ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); }
7315 ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); }
7316 HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); }
7317 HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); }
7318 HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); }
7319 HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); }
7320 HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); }
7321 
7322 
7323 /* IAudioClient */
7324 typedef struct
7325 {
7326  /* IUnknown */
7327  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject);
7328  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis);
7329  ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis);
7330 
7331  /* IAudioClient */
7332  HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
7333  HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames);
7334  HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency);
7335  HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames);
7336  HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
7337  HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat);
7338  HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
7339  HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis);
7340  HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis);
7341  HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis);
7342  HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle);
7343  HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp);
7344 } ma_IAudioClientVtbl;
7345 struct ma_IAudioClient
7346 {
7347  ma_IAudioClientVtbl* lpVtbl;
7348 };
7349 HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
7350 ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
7351 ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); }
7352 HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
7353 HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
7354 HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
7355 HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
7356 HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
7357 HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
7358 HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
7359 HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); }
7360 HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); }
7361 HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); }
7362 HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
7363 HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
7364 
7365 /* IAudioClient2 */
7366 typedef struct
7367 {
7368  /* IUnknown */
7369  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject);
7370  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis);
7371  ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis);
7372 
7373  /* IAudioClient */
7374  HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
7375  HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames);
7376  HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency);
7377  HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames);
7378  HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
7379  HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat);
7380  HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
7381  HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis);
7382  HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis);
7383  HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis);
7384  HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle);
7385  HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp);
7386 
7387  /* IAudioClient2 */
7388  HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
7389  HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties);
7390  HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
7391 } ma_IAudioClient2Vtbl;
7392 struct ma_IAudioClient2
7393 {
7394  ma_IAudioClient2Vtbl* lpVtbl;
7395 };
7396 HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
7397 ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); }
7398 ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); }
7399 HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
7400 HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
7401 HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
7402 HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
7403 HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
7404 HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
7405 HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
7406 HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); }
7407 HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); }
7408 HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); }
7409 HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
7410 HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
7411 HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
7412 HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
7413 HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); }
7414 
7415 
7416 /* IAudioClient3 */
7417 typedef struct
7418 {
7419  /* IUnknown */
7420  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject);
7421  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis);
7422  ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis);
7423 
7424  /* IAudioClient */
7425  HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
7426  HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames);
7427  HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency);
7428  HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames);
7429  HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch);
7430  HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat);
7431  HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
7432  HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis);
7433  HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis);
7434  HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis);
7435  HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle);
7436  HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp);
7437 
7438  /* IAudioClient2 */
7439  HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
7440  HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties);
7441  HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
7442 
7443  /* IAudioClient3 */
7444  HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, UINT32* pDefaultPeriodInFrames, UINT32* pFundamentalPeriodInFrames, UINT32* pMinPeriodInFrames, UINT32* pMaxPeriodInFrames);
7445  HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, UINT32* pCurrentPeriodInFrames);
7446  HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, UINT32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
7447 } ma_IAudioClient3Vtbl;
7448 struct ma_IAudioClient3
7449 {
7450  ma_IAudioClient3Vtbl* lpVtbl;
7451 };
7452 HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
7453 ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); }
7454 ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); }
7455 HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
7456 HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
7457 HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
7458 HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
7459 HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
7460 HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
7461 HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
7462 HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); }
7463 HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); }
7464 HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); }
7465 HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
7466 HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
7467 HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
7468 HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
7469 HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); }
7470 HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, UINT32* pDefaultPeriodInFrames, UINT32* pFundamentalPeriodInFrames, UINT32* pMinPeriodInFrames, UINT32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); }
7471 HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, UINT32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); }
7472 HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, UINT32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); }
7473 
7474 
7475 /* IAudioRenderClient */
7476 typedef struct
7477 {
7478  /* IUnknown */
7479  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject);
7480  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis);
7481  ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis);
7482 
7483  /* IAudioRenderClient */
7484  HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData);
7485  HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags);
7486 } ma_IAudioRenderClientVtbl;
7487 struct ma_IAudioRenderClient
7488 {
7489  ma_IAudioRenderClientVtbl* lpVtbl;
7490 };
7491 HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
7492 ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
7493 ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); }
7494 HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); }
7495 HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); }
7496 
7497 
7498 /* IAudioCaptureClient */
7499 typedef struct
7500 {
7501  /* IUnknown */
7502  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject);
7503  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis);
7504  ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis);
7505 
7506  /* IAudioRenderClient */
7507  HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition);
7508  HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead);
7509  HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket);
7510 } ma_IAudioCaptureClientVtbl;
7511 struct ma_IAudioCaptureClient
7512 {
7513  ma_IAudioCaptureClientVtbl* lpVtbl;
7514 };
7515 HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
7516 ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
7517 ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); }
7518 HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); }
7519 HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); }
7520 HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); }
7521 
7522 #ifndef MA_WIN32_DESKTOP
7523 #include <mmdeviceapi.h>
7524 typedef struct ma_completion_handler_uwp ma_completion_handler_uwp;
7525 
7526 typedef struct
7527 {
7528  /* IUnknown */
7529  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject);
7530  ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis);
7531  ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis);
7532 
7533  /* IActivateAudioInterfaceCompletionHandler */
7534  HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation);
7535 } ma_completion_handler_uwp_vtbl;
7536 struct ma_completion_handler_uwp
7537 {
7538  ma_completion_handler_uwp_vtbl* lpVtbl;
7540  HANDLE hEvent;
7541 };
7542 
7543 HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject)
7544 {
7545  /*
7546  We need to "implement" IAgileObject which is just an indicator that's used internally by WASAPI for some multithreading management. To
7547  "implement" this, we just make sure we return pThis when the IAgileObject is requested.
7548  */
7549  if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IActivateAudioInterfaceCompletionHandler) && !ma_is_guid_equal(riid, &MA_IID_IAgileObject)) {
7550  *ppObject = NULL;
7551  return E_NOINTERFACE;
7552  }
7553 
7554  /* Getting here means the IID is IUnknown or IMMNotificationClient. */
7555  *ppObject = (void*)pThis;
7556  ((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis);
7557  return S_OK;
7558 }
7559 
7560 ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis)
7561 {
7562  return (ULONG)ma_atomic_increment_32(&pThis->counter);
7563 }
7564 
7565 ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis)
7566 {
7567  ma_uint32 newRefCount = ma_atomic_decrement_32(&pThis->counter);
7568  if (newRefCount == 0) {
7569  return 0; /* We don't free anything here because we never allocate the object on the heap. */
7570  }
7571 
7572  return (ULONG)newRefCount;
7573 }
7574 
7575 HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation)
7576 {
7577  (void)pActivateOperation;
7578  SetEvent(pThis->hEvent);
7579  return S_OK;
7580 }
7581 
7582 
7583 static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = {
7584  ma_completion_handler_uwp_QueryInterface,
7585  ma_completion_handler_uwp_AddRef,
7586  ma_completion_handler_uwp_Release,
7587  ma_completion_handler_uwp_ActivateCompleted
7588 };
7589 
7590 ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler)
7591 {
7592  ma_assert(pHandler != NULL);
7593  ma_zero_object(pHandler);
7594 
7595  pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance;
7596  pHandler->counter = 1;
7597  pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
7598  if (pHandler->hEvent == NULL) {
7599  return MA_ERROR;
7600  }
7601 
7602  return MA_SUCCESS;
7603 }
7604 
7605 void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler)
7606 {
7607  if (pHandler->hEvent != NULL) {
7608  CloseHandle(pHandler->hEvent);
7609  }
7610 }
7611 
7612 void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler)
7613 {
7614  WaitForSingleObject(pHandler->hEvent, INFINITE);
7615 }
7616 #endif /* !MA_WIN32_DESKTOP */
7617 
7618 /* We need a virtual table for our notification client object that's used for detecting changes to the default device. */
7619 #ifdef MA_WIN32_DESKTOP
7620 HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject)
7621 {
7622  /*
7623  We care about two interfaces - IUnknown and IMMNotificationClient. If the requested IID is something else
7624  we just return E_NOINTERFACE. Otherwise we need to increment the reference counter and return S_OK.
7625  */
7626  if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) {
7627  *ppObject = NULL;
7628  return E_NOINTERFACE;
7629  }
7630 
7631  /* Getting here means the IID is IUnknown or IMMNotificationClient. */
7632  *ppObject = (void*)pThis;
7633  ((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis);
7634  return S_OK;
7635 }
7636 
7637 ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis)
7638 {
7639  return (ULONG)ma_atomic_increment_32(&pThis->counter);
7640 }
7641 
7642 ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis)
7643 {
7644  ma_uint32 newRefCount = ma_atomic_decrement_32(&pThis->counter);
7645  if (newRefCount == 0) {
7646  return 0; /* We don't free anything here because we never allocate the object on the heap. */
7647  }
7648 
7649  return (ULONG)newRefCount;
7650 }
7651 
7652 
7653 HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState)
7654 {
7655 #ifdef MA_DEBUG_OUTPUT
7656  printf("IMMNotificationClient_OnDeviceStateChanged(pDeviceID=%S, dwNewState=%u)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)", (unsigned int)dwNewState);
7657 #endif
7658 
7659  (void)pThis;
7660  (void)pDeviceID;
7661  (void)dwNewState;
7662  return S_OK;
7663 }
7664 
7665 HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
7666 {
7667 #ifdef MA_DEBUG_OUTPUT
7668  printf("IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");
7669 #endif
7670 
7671  /* We don't need to worry about this event for our purposes. */
7672  (void)pThis;
7673  (void)pDeviceID;
7674  return S_OK;
7675 }
7676 
7677 HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID)
7678 {
7679 #ifdef MA_DEBUG_OUTPUT
7680  printf("IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");
7681 #endif
7682 
7683  /* We don't need to worry about this event for our purposes. */
7684  (void)pThis;
7685  (void)pDeviceID;
7686  return S_OK;
7687 }
7688 
7689 HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID)
7690 {
7691 #ifdef MA_DEBUG_OUTPUT
7692  printf("IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");
7693 #endif
7694 
7695  /* We only ever use the eConsole role in miniaudio. */
7696  if (role != ma_eConsole) {
7697  return S_OK;
7698  }
7699 
7700  /* We only care about devices with the same data flow and role as the current device. */
7701  if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) ||
7702  (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture)) {
7703  return S_OK;
7704  }
7705 
7706  /*
7707  Not currently supporting automatic stream routing in exclusive mode. This is not working correctly on my machine due to
7708  AUDCLNT_E_DEVICE_IN_USE errors when reinitializing the device. If this is a bug in miniaudio, we can try re-enabling this once
7709  it's fixed.
7710  */
7711  if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) ||
7712  (dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) {
7713  return S_OK;
7714  }
7715 
7716  /*
7717  We don't change the device here - we change it in the worker thread to keep synchronization simple. To do this I'm just setting a flag to
7718  indicate that the default device has changed. Loopback devices are treated as capture devices so we need to do a bit of a dance to handle
7719  that properly.
7720  */
7721  if (dataFlow == ma_eRender && pThis->pDevice->type != ma_device_type_loopback) {
7722  ma_atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultPlaybackDeviceChanged, MA_TRUE);
7723  }
7724  if (dataFlow == ma_eCapture || pThis->pDevice->type == ma_device_type_loopback) {
7725  ma_atomic_exchange_32(&pThis->pDevice->wasapi.hasDefaultCaptureDeviceChanged, MA_TRUE);
7726  }
7727 
7728  (void)pDefaultDeviceID;
7729  return S_OK;
7730 }
7731 
7732 HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key)
7733 {
7734 #ifdef MA_DEBUG_OUTPUT
7735  printf("IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");
7736 #endif
7737 
7738  (void)pThis;
7739  (void)pDeviceID;
7740  (void)key;
7741  return S_OK;
7742 }
7743 
7744 static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = {
7745  ma_IMMNotificationClient_QueryInterface,
7746  ma_IMMNotificationClient_AddRef,
7747  ma_IMMNotificationClient_Release,
7748  ma_IMMNotificationClient_OnDeviceStateChanged,
7749  ma_IMMNotificationClient_OnDeviceAdded,
7750  ma_IMMNotificationClient_OnDeviceRemoved,
7751  ma_IMMNotificationClient_OnDefaultDeviceChanged,
7752  ma_IMMNotificationClient_OnPropertyValueChanged
7753 };
7754 #endif /* MA_WIN32_DESKTOP */
7755 
7756 #ifdef MA_WIN32_DESKTOP
7757 typedef ma_IMMDevice ma_WASAPIDeviceInterface;
7758 #else
7759 typedef ma_IUnknown ma_WASAPIDeviceInterface;
7760 #endif
7761 
7762 
7763 
7764 ma_bool32 ma_context_is_device_id_equal__wasapi(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
7765 {
7766  ma_assert(pContext != NULL);
7767  ma_assert(pID0 != NULL);
7768  ma_assert(pID1 != NULL);
7769  (void)pContext;
7770 
7771  return memcmp(pID0->wasapi, pID1->wasapi, sizeof(pID0->wasapi)) == 0;
7772 }
7773 
7774 void ma_set_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, ma_device_info* pInfo)
7775 {
7776  ma_assert(pWF != NULL);
7777  ma_assert(pInfo != NULL);
7778 
7779  pInfo->formatCount = 1;
7780  pInfo->formats[0] = ma_format_from_WAVEFORMATEX(pWF);
7781  pInfo->minChannels = pWF->nChannels;
7782  pInfo->maxChannels = pWF->nChannels;
7783  pInfo->minSampleRate = pWF->nSamplesPerSec;
7784  pInfo->maxSampleRate = pWF->nSamplesPerSec;
7785 }
7786 
7787 ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_share_mode shareMode, ma_device_info* pInfo)
7788 {
7789  ma_assert(pAudioClient != NULL);
7790  ma_assert(pInfo != NULL);
7791 
7792  /* We use a different technique to retrieve the device information depending on whether or not we are using shared or exclusive mode. */
7793  if (shareMode == ma_share_mode_shared) {
7794  /* Shared Mode. We use GetMixFormat() here. */
7795  WAVEFORMATEX* pWF = NULL;
7796  HRESULT hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (WAVEFORMATEX**)&pWF);
7797  if (SUCCEEDED(hr)) {
7798  ma_set_device_info_from_WAVEFORMATEX(pWF, pInfo);
7799  return MA_SUCCESS;
7800  } else {
7801  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7802  }
7803  } else {
7804  /* Exlcusive Mode. We repeatedly call IsFormatSupported() here. This is not currently support on UWP. */
7805 #ifdef MA_WIN32_DESKTOP
7806  /*
7807  The first thing to do is get the format from PKEY_AudioEngine_DeviceFormat. This should give us a channel count we assume is
7808  correct which will simplify our searching.
7809  */
7810  ma_IPropertyStore *pProperties;
7811  HRESULT hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties);
7812  if (SUCCEEDED(hr)) {
7813  PROPVARIANT var;
7814  ma_PropVariantInit(&var);
7815 
7816  hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var);
7817  if (SUCCEEDED(hr)) {
7818  WAVEFORMATEX* pWF = (WAVEFORMATEX*)var.blob.pBlobData;
7819  ma_set_device_info_from_WAVEFORMATEX(pWF, pInfo);
7820 
7821  /*
7822  In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format
7823  first. If this fails, fall back to a search.
7824  */
7825  hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL);
7826  ma_PropVariantClear(pContext, &var);
7827 
7828  if (FAILED(hr)) {
7829  /*
7830  The format returned by PKEY_AudioEngine_DeviceFormat is not supported, so fall back to a search. We assume the channel
7831  count returned by MA_PKEY_AudioEngine_DeviceFormat is valid and correct. For simplicity we're only returning one format.
7832  */
7833  ma_uint32 channels = pInfo->minChannels;
7834  ma_format formatsToSearch[] = {
7835  ma_format_s16,
7836  ma_format_s24,
7837  /*ma_format_s24_32,*/
7838  ma_format_f32,
7839  ma_format_s32,
7840  ma_format_u8
7841  };
7842  ma_channel defaultChannelMap[MA_MAX_CHANNELS];
7843  WAVEFORMATEXTENSIBLE wf;
7844  ma_bool32 found;
7845  ma_uint32 iFormat;
7846 
7847  ma_get_standard_channel_map(ma_standard_channel_map_microsoft, channels, defaultChannelMap);
7848 
7849  ma_zero_object(&wf);
7850  wf.Format.cbSize = sizeof(wf);
7851  wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
7852  wf.Format.nChannels = (WORD)channels;
7853  wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels);
7854 
7855  found = MA_FALSE;
7856  for (iFormat = 0; iFormat < ma_countof(formatsToSearch); ++iFormat) {
7857  ma_format format = formatsToSearch[iFormat];
7858  ma_uint32 iSampleRate;
7859 
7860  wf.Format.wBitsPerSample = (WORD)ma_get_bytes_per_sample(format)*8;
7861  wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
7862  wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
7863  wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.Format.wBitsPerSample;
7864  if (format == ma_format_f32) {
7865  wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
7866  } else {
7867  wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
7868  }
7869 
7870  for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) {
7871  wf.Format.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate];
7872 
7873  hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL);
7874  if (SUCCEEDED(hr)) {
7875  ma_set_device_info_from_WAVEFORMATEX((WAVEFORMATEX*)&wf, pInfo);
7876  found = MA_TRUE;
7877  break;
7878  }
7879  }
7880 
7881  if (found) {
7882  break;
7883  }
7884  }
7885 
7886  if (!found) {
7887  ma_IPropertyStore_Release(pProperties);
7888  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to find suitable device format for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7889  }
7890  }
7891  } else {
7892  ma_IPropertyStore_Release(pProperties);
7893  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve device format for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7894  }
7895 
7896  ma_IPropertyStore_Release(pProperties);
7897  } else {
7898  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to open property store for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7899  }
7900 
7901  return MA_SUCCESS;
7902 #else
7903  /* Exclusive mode not fully supported in UWP right now. */
7904  return MA_ERROR;
7905 #endif
7906  }
7907 }
7908 
7909 #ifdef MA_WIN32_DESKTOP
7910 ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice)
7911 {
7912  ma_IMMDeviceEnumerator* pDeviceEnumerator;
7913  HRESULT hr;
7914 
7915  ma_assert(pContext != NULL);
7916  ma_assert(ppMMDevice != NULL);
7917 
7918  hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
7919  if (FAILED(hr)) {
7920  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.", MA_FAILED_TO_INIT_BACKEND);
7921  }
7922 
7923  if (pDeviceID == NULL) {
7924  hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice);
7925  } else {
7926  hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice);
7927  }
7928 
7929  ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
7930  if (FAILED(hr)) {
7931  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7932  }
7933 
7934  return MA_SUCCESS;
7935 }
7936 
7937 ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_share_mode shareMode, ma_bool32 onlySimpleInfo, ma_device_info* pInfo)
7938 {
7939  LPWSTR id;
7940  HRESULT hr;
7941 
7942  ma_assert(pContext != NULL);
7943  ma_assert(pMMDevice != NULL);
7944  ma_assert(pInfo != NULL);
7945 
7946  /* ID. */
7947  hr = ma_IMMDevice_GetId(pMMDevice, &id);
7948  if (SUCCEEDED(hr)) {
7949  size_t idlen = wcslen(id);
7950  if (idlen+1 > ma_countof(pInfo->id.wasapi)) {
7951  ma_CoTaskMemFree(pContext, id);
7952  ma_assert(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. */
7953  return MA_ERROR;
7954  }
7955 
7956  ma_copy_memory(pInfo->id.wasapi, id, idlen * sizeof(wchar_t));
7957  pInfo->id.wasapi[idlen] = '\0';
7958 
7959  ma_CoTaskMemFree(pContext, id);
7960  }
7961 
7962  {
7963  ma_IPropertyStore *pProperties;
7964  hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);
7965  if (SUCCEEDED(hr)) {
7966  PROPVARIANT var;
7967 
7968  /* Description / Friendly Name */
7969  ma_PropVariantInit(&var);
7970  hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var);
7971  if (SUCCEEDED(hr)) {
7972  WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
7973  ma_PropVariantClear(pContext, &var);
7974  }
7975 
7976  ma_IPropertyStore_Release(pProperties);
7977  }
7978  }
7979 
7980  /* Format */
7981  if (!onlySimpleInfo) {
7982  ma_IAudioClient* pAudioClient;
7983  hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
7984  if (SUCCEEDED(hr)) {
7985  ma_result result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, shareMode, pInfo);
7986 
7987  ma_IAudioClient_Release(pAudioClient);
7988  return result;
7989  } else {
7990  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
7991  }
7992  }
7993 
7994  return MA_SUCCESS;
7995 }
7996 
7997 ma_result ma_context_enumerate_device_collection__wasapi(ma_context* pContext, ma_IMMDeviceCollection* pDeviceCollection, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData)
7998 {
7999  UINT deviceCount;
8000  HRESULT hr;
8001  ma_uint32 iDevice;
8002 
8003  ma_assert(pContext != NULL);
8004  ma_assert(callback != NULL);
8005 
8006  hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount);
8007  if (FAILED(hr)) {
8008  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get playback device count.", MA_NO_DEVICE);
8009  }
8010 
8011  for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
8012  ma_device_info deviceInfo;
8013  ma_IMMDevice* pMMDevice;
8014 
8015  ma_zero_object(&deviceInfo);
8016 
8017  hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice);
8018  if (SUCCEEDED(hr)) {
8019  ma_result result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, ma_share_mode_shared, MA_TRUE, &deviceInfo); /* MA_TRUE = onlySimpleInfo. */
8020 
8021  ma_IMMDevice_Release(pMMDevice);
8022  if (result == MA_SUCCESS) {
8023  ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
8024  if (cbResult == MA_FALSE) {
8025  break;
8026  }
8027  }
8028  }
8029  }
8030 
8031  return MA_SUCCESS;
8032 }
8033 #endif
8034 
8035 #ifdef MA_WIN32_DESKTOP
8036 ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice)
8037 {
8038  ma_result result;
8039  HRESULT hr;
8040 
8041  ma_assert(pContext != NULL);
8042  ma_assert(ppAudioClient != NULL);
8043  ma_assert(ppMMDevice != NULL);
8044 
8045  result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice);
8046  if (result != MA_SUCCESS) {
8047  return result;
8048  }
8049 
8050  hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)ppAudioClient);
8051  if (FAILED(hr)) {
8053  }
8054 
8055  return MA_SUCCESS;
8056 }
8057 #else
8058 ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface)
8059 {
8060  ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
8061  ma_completion_handler_uwp completionHandler;
8062  IID iid;
8063  LPOLESTR iidStr;
8064  HRESULT hr;
8065  ma_result result;
8066  HRESULT activateResult;
8067  ma_IUnknown* pActivatedInterface;
8068 
8069  ma_assert(pContext != NULL);
8070  ma_assert(ppAudioClient != NULL);
8071 
8072  if (pDeviceID != NULL) {
8073  ma_copy_memory(&iid, pDeviceID->wasapi, sizeof(iid));
8074  } else {
8075  if (deviceType == ma_device_type_playback) {
8076  iid = MA_IID_DEVINTERFACE_AUDIO_RENDER;
8077  } else {
8078  iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE;
8079  }
8080  }
8081 
8082 #if defined(__cplusplus)
8083  hr = StringFromIID(iid, &iidStr);
8084 #else
8085  hr = StringFromIID(&iid, &iidStr);
8086 #endif
8087  if (FAILED(hr)) {
8088  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.", MA_OUT_OF_MEMORY);
8089  }
8090 
8091  result = ma_completion_handler_uwp_init(&completionHandler);
8092  if (result != MA_SUCCESS) {
8093  ma_CoTaskMemFree(pContext, iidStr);
8094  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
8095  }
8096 
8097 #if defined(__cplusplus)
8098  hr = ActivateAudioInterfaceAsync(iidStr, MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
8099 #else
8100  hr = ActivateAudioInterfaceAsync(iidStr, &MA_IID_IAudioClient, NULL, (IActivateAudioInterfaceCompletionHandler*)&completionHandler, (IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
8101 #endif
8102  if (FAILED(hr)) {
8103  ma_completion_handler_uwp_uninit(&completionHandler);
8104  ma_CoTaskMemFree(pContext, iidStr);
8105  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
8106  }
8107 
8108  ma_CoTaskMemFree(pContext, iidStr);
8109 
8110  /* Wait for the async operation for finish. */
8111  ma_completion_handler_uwp_wait(&completionHandler);
8112  ma_completion_handler_uwp_uninit(&completionHandler);
8113 
8114  hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
8115  ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
8116 
8117  if (FAILED(hr) || FAILED(activateResult)) {
8118  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
8119  }
8120 
8121  /* Here is where we grab the IAudioClient interface. */
8122  hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient);
8123  if (FAILED(hr)) {
8124  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
8125  }
8126 
8127  if (ppActivatedInterface) {
8128  *ppActivatedInterface = pActivatedInterface;
8129  } else {
8130  ma_IUnknown_Release(pActivatedInterface);
8131  }
8132 
8133  return MA_SUCCESS;
8134 }
8135 #endif
8136 
8137 ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface)
8138 {
8139 #ifdef MA_WIN32_DESKTOP
8140  return ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface);
8141 #else
8142  return ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, ppAudioClient, ppDeviceInterface);
8143 #endif
8144 }
8145 
8146 
8147 ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
8148 {
8149  /* Different enumeration for desktop and UWP. */
8150 #ifdef MA_WIN32_DESKTOP
8151  /* Desktop */
8152  HRESULT hr;
8153  ma_IMMDeviceEnumerator* pDeviceEnumerator;
8154  ma_IMMDeviceCollection* pDeviceCollection;
8155 
8156  hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
8157  if (FAILED(hr)) {
8158  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
8159  }
8160 
8161  /* Playback. */
8162  hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_eRender, MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);
8163  if (SUCCEEDED(hr)) {
8164  ma_context_enumerate_device_collection__wasapi(pContext, pDeviceCollection, ma_device_type_playback, callback, pUserData);
8165  ma_IMMDeviceCollection_Release(pDeviceCollection);
8166  }
8167 
8168  /* Capture. */
8169  hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_eCapture, MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);
8170  if (SUCCEEDED(hr)) {
8171  ma_context_enumerate_device_collection__wasapi(pContext, pDeviceCollection, ma_device_type_capture, callback, pUserData);
8172  ma_IMMDeviceCollection_Release(pDeviceCollection);
8173  }
8174 
8175  ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
8176 #else
8177  /*
8178  UWP
8179 
8180  The MMDevice API is only supported on desktop applications. For now, while I'm still figuring out how to properly enumerate
8181  over devices without using MMDevice, I'm restricting devices to defaults.
8182 
8183  Hint: DeviceInformation::FindAllAsync() with DeviceClass.AudioCapture/AudioRender. https://blogs.windows.com/buildingapps/2014/05/15/real-time-audio-in-windows-store-and-windows-phone-apps/
8184  */
8185  if (callback) {
8186  ma_bool32 cbResult = MA_TRUE;
8187 
8188  /* Playback. */
8189  if (cbResult) {
8190  ma_device_info deviceInfo;
8191  ma_zero_object(&deviceInfo);
8192  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
8193  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
8194  }
8195 
8196  /* Capture. */
8197  if (cbResult) {
8198  ma_device_info deviceInfo;
8199  ma_zero_object(&deviceInfo);
8200  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
8201  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
8202  }
8203  }
8204 #endif
8205 
8206  return MA_SUCCESS;
8207 }
8208 
8209 ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
8210 {
8211 #ifdef MA_WIN32_DESKTOP
8212  ma_IMMDevice* pMMDevice = NULL;
8213  ma_result result;
8214 
8215  result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);
8216  if (result != MA_SUCCESS) {
8217  return result;
8218  }
8219 
8220  result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, shareMode, MA_FALSE, pDeviceInfo); /* MA_FALSE = !onlySimpleInfo. */
8221 
8222  ma_IMMDevice_Release(pMMDevice);
8223  return result;
8224 #else
8225  ma_IAudioClient* pAudioClient;
8226  ma_result result;
8227 
8228  /* UWP currently only uses default devices. */
8229  if (deviceType == ma_device_type_playback) {
8230  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
8231  } else {
8232  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
8233  }
8234 
8235  /* Not currently supporting exclusive mode on UWP. */
8236  if (shareMode == ma_share_mode_exclusive) {
8237  return MA_ERROR;
8238  }
8239 
8240  result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, &pAudioClient, NULL);
8241  if (result != MA_SUCCESS) {
8242  return result;
8243  }
8244 
8245  result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, shareMode, pDeviceInfo);
8246 
8247  ma_IAudioClient_Release(pAudioClient);
8248  return result;
8249 #endif
8250 }
8251 
8252 void ma_device_uninit__wasapi(ma_device* pDevice)
8253 {
8254  ma_assert(pDevice != NULL);
8255 
8256 #ifdef MA_WIN32_DESKTOP
8257  if (pDevice->wasapi.pDeviceEnumerator) {
8258  ((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient);
8259  ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator);
8260  }
8261 #endif
8262 
8263  if (pDevice->wasapi.pRenderClient) {
8264  ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
8265  }
8266  if (pDevice->wasapi.pCaptureClient) {
8267  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
8268  }
8269 
8270  if (pDevice->wasapi.pAudioClientPlayback) {
8271  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
8272  }
8273  if (pDevice->wasapi.pAudioClientCapture) {
8274  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
8275  }
8276 
8277  if (pDevice->wasapi.hEventPlayback) {
8278  CloseHandle(pDevice->wasapi.hEventPlayback);
8279  }
8280  if (pDevice->wasapi.hEventCapture) {
8281  CloseHandle(pDevice->wasapi.hEventCapture);
8282  }
8283 }
8284 
8285 
8286 typedef struct
8287 {
8288  /* Input. */
8289  ma_format formatIn;
8290  ma_uint32 channelsIn;
8291  ma_uint32 sampleRateIn;
8292  ma_channel channelMapIn[MA_MAX_CHANNELS];
8293  ma_uint32 bufferSizeInFramesIn;
8294  ma_uint32 bufferSizeInMillisecondsIn;
8295  ma_uint32 periodsIn;
8296  ma_bool32 usingDefaultFormat;
8297  ma_bool32 usingDefaultChannels;
8298  ma_bool32 usingDefaultSampleRate;
8299  ma_bool32 usingDefaultChannelMap;
8300  ma_share_mode shareMode;
8301  ma_bool32 noAutoConvertSRC;
8302  ma_bool32 noDefaultQualitySRC;
8303 
8304  /* Output. */
8305  ma_IAudioClient* pAudioClient;
8306  ma_IAudioRenderClient* pRenderClient;
8307  ma_IAudioCaptureClient* pCaptureClient;
8308  ma_format formatOut;
8309  ma_uint32 channelsOut;
8310  ma_uint32 sampleRateOut;
8311  ma_channel channelMapOut[MA_MAX_CHANNELS];
8312  ma_uint32 bufferSizeInFramesOut;
8313  ma_uint32 periodSizeInFramesOut;
8314  ma_uint32 periodsOut;
8315  ma_bool32 usingAudioClient3;
8316  char deviceName[256];
8317 } ma_device_init_internal_data__wasapi;
8318 
8319 ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__wasapi* pData)
8320 {
8321  HRESULT hr;
8323  const char* errorMsg = "";
8324  MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
8325  DWORD streamFlags = 0;
8326  MA_REFERENCE_TIME bufferDurationInMicroseconds;
8327  ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE;
8328  WAVEFORMATEXTENSIBLE wf = {0};
8329  ma_WASAPIDeviceInterface* pDeviceInterface = NULL;
8330  ma_IAudioClient2* pAudioClient2;
8331  ma_uint32 nativeSampleRate;
8332 
8333  ma_assert(pContext != NULL);
8334  ma_assert(pData != NULL);
8335 
8336  /* This function is only used to initialize one device type: either playback, capture or loopback. Never full-duplex. */
8337  if (deviceType == ma_device_type_duplex) {
8338  return MA_INVALID_ARGS;
8339  }
8340 
8341  pData->pAudioClient = NULL;
8342  pData->pRenderClient = NULL;
8343  pData->pCaptureClient = NULL;
8344 
8345  streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
8346  if (!pData->noAutoConvertSRC && !pData->usingDefaultSampleRate && pData->shareMode != ma_share_mode_exclusive) { /* <-- Exclusive streams must use the native sample rate. */
8347  streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
8348  }
8349  if (!pData->noDefaultQualitySRC && !pData->usingDefaultSampleRate && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) {
8350  streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
8351  }
8352  if (deviceType == ma_device_type_loopback) {
8353  streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK;
8354  }
8355 
8356  result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, &pData->pAudioClient, &pDeviceInterface);
8357  if (result != MA_SUCCESS) {
8358  goto done;
8359  }
8360 
8361 
8362  /* Try enabling hardware offloading. */
8363  hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2);
8364  if (SUCCEEDED(hr)) {
8365  BOOL isHardwareOffloadingSupported = 0;
8366  hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported);
8367  if (SUCCEEDED(hr) && isHardwareOffloadingSupported) {
8368  ma_AudioClientProperties clientProperties;
8369  ma_zero_object(&clientProperties);
8370  clientProperties.cbSize = sizeof(clientProperties);
8371  clientProperties.bIsOffload = 1;
8372  clientProperties.eCategory = MA_AudioCategory_Other;
8373  ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties);
8374  }
8375 
8376  pAudioClient2->lpVtbl->Release(pAudioClient2);
8377  }
8378 
8379  /* Here is where we try to determine the best format to use with the device. If the client if wanting exclusive mode, first try finding the best format for that. If this fails, fall back to shared mode. */
8381  if (pData->shareMode == ma_share_mode_exclusive) {
8382  #ifdef MA_WIN32_DESKTOP
8383  /* In exclusive mode on desktop we always use the backend's native format. */
8384  ma_IPropertyStore* pStore = NULL;
8385  hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore);
8386  if (SUCCEEDED(hr)) {
8387  PROPVARIANT prop;
8388  ma_PropVariantInit(&prop);
8389  hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop);
8390  if (SUCCEEDED(hr)) {
8391  WAVEFORMATEX* pActualFormat = (WAVEFORMATEX*)prop.blob.pBlobData;
8392  hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL);
8393  if (SUCCEEDED(hr)) {
8394  ma_copy_memory(&wf, pActualFormat, sizeof(WAVEFORMATEXTENSIBLE));
8395  }
8396 
8397  ma_PropVariantClear(pContext, &prop);
8398  }
8399 
8400  ma_IPropertyStore_Release(pStore);
8401  }
8402  #else
8403  /*
8404  I do not know how to query the device's native format on UWP so for now I'm just disabling support for
8405  exclusive mode. The alternative is to enumerate over different formats and check IsFormatSupported()
8406  until you find one that works.
8407 
8408  TODO: Add support for exclusive mode to UWP.
8409  */
8410  hr = S_FALSE;
8411  #endif
8412 
8413  if (hr == S_OK) {
8414  shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE;
8415  result = MA_SUCCESS;
8416  } else {
8418  }
8419  } else {
8420  /* In shared mode we are always using the format reported by the operating system. */
8421  WAVEFORMATEXTENSIBLE* pNativeFormat = NULL;
8422  hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (WAVEFORMATEX**)&pNativeFormat);
8423  if (hr != S_OK) {
8425  } else {
8426  ma_copy_memory(&wf, pNativeFormat, sizeof(wf));
8427  result = MA_SUCCESS;
8428  }
8429 
8430  ma_CoTaskMemFree(pContext, pNativeFormat);
8431 
8432  shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
8433  }
8434 
8435  /* Return an error if we still haven't found a format. */
8436  if (result != MA_SUCCESS) {
8437  errorMsg = "[WASAPI] Failed to find best device mix format.";
8438  goto done;
8439  }
8440 
8441  /*
8442  Override the native sample rate with the one requested by the caller, but only if we're not using the default sample rate. We'll use
8443  WASAPI to perform the sample rate conversion.
8444  */
8445  nativeSampleRate = wf.Format.nSamplesPerSec;
8446  if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) {
8447  wf.Format.nSamplesPerSec = pData->sampleRateIn;
8448  wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign;
8449  }
8450 
8451  pData->formatOut = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)&wf);
8452  pData->channelsOut = wf.Format.nChannels;
8453  pData->sampleRateOut = wf.Format.nSamplesPerSec;
8454 
8455  /* Get the internal channel map based on the channel mask. */
8456  ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut);
8457 
8458  /* If we're using a default buffer size we need to calculate it based on the efficiency of the system. */
8459  pData->periodsOut = pData->periodsIn;
8460  pData->bufferSizeInFramesOut = pData->bufferSizeInFramesIn;
8461  if (pData->bufferSizeInFramesOut == 0) {
8462  pData->bufferSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->bufferSizeInMillisecondsIn, wf.Format.nSamplesPerSec);
8463  }
8464 
8465  bufferDurationInMicroseconds = ((ma_uint64)pData->bufferSizeInFramesOut * 1000 * 1000) / wf.Format.nSamplesPerSec;
8466 
8467 
8468  /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */
8469  if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
8470  MA_REFERENCE_TIME bufferDuration = (bufferDurationInMicroseconds / pData->periodsOut) * 10;
8471 
8472  /*
8473  If the periodicy is too small, Initialize() will fail with AUDCLNT_E_INVALID_DEVICE_PERIOD. In this case we should just keep increasing
8474  it and trying it again.
8475  */
8476  hr = E_FAIL;
8477  for (;;) {
8478  hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
8479  if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) {
8480  if (bufferDuration > 500*10000) {
8481  break;
8482  } else {
8483  if (bufferDuration == 0) { /* <-- Just a sanity check to prevent an infinit loop. Should never happen, but it makes me feel better. */
8484  break;
8485  }
8486 
8487  bufferDuration = bufferDuration * 2;
8488  continue;
8489  }
8490  } else {
8491  break;
8492  }
8493  }
8494 
8495  if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
8496  ma_uint32 bufferSizeInFrames;
8497  hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
8498  if (SUCCEEDED(hr)) {
8499  bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5);
8500 
8501  /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */
8502  ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
8503 
8504  #ifdef MA_WIN32_DESKTOP
8505  hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient);
8506  #else
8507  hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient);
8508  #endif
8509 
8510  if (SUCCEEDED(hr)) {
8511  hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL);
8512  }
8513  }
8514  }
8515 
8516  if (FAILED(hr)) {
8517  /* Failed to initialize in exclusive mode. Don't fall back to shared mode - instead tell the client about it. They can reinitialize in shared mode if they want. */
8518  if (hr == E_ACCESSDENIED) {
8519  errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED;
8520  } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
8521  errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_DEVICE_BUSY;
8522  } else {
8523  errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = MA_SHARE_MODE_NOT_SUPPORTED;
8524  }
8525  goto done;
8526  }
8527  }
8528 
8529  if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) {
8530  /*
8531  Low latency shared mode via IAudioClient3.
8532 
8533  NOTE
8534  ====
8535  Contrary to the documentation on MSDN (https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient3-initializesharedaudiostream), the
8536  use of AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM and AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY with IAudioClient3_InitializeSharedAudioStream() absolutely does not work. Using
8537  any of these flags will result in HRESULT code 0x88890021. The other problem is that calling IAudioClient3_GetSharedModeEnginePeriod() with a sample rate different to
8538  that returned by IAudioClient_GetMixFormat() also results in an error. I'm therefore disabling low-latency shared mode with AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM.
8539  */
8540 #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE
8541  if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.Format.nSamplesPerSec) {
8542  ma_IAudioClient3* pAudioClient3 = NULL;
8543  hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3);
8544  if (SUCCEEDED(hr)) {
8545  UINT32 defaultPeriodInFrames;
8546  UINT32 fundamentalPeriodInFrames;
8547  UINT32 minPeriodInFrames;
8548  UINT32 maxPeriodInFrames;
8549  hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames);
8550  if (SUCCEEDED(hr)) {
8551  UINT32 desiredPeriodInFrames = pData->bufferSizeInFramesOut / pData->periodsOut;
8552  UINT32 actualPeriodInFrames = desiredPeriodInFrames;
8553 
8554  /* Make sure the period size is a multiple of fundamentalPeriodInFrames. */
8555  actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames;
8556  actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames;
8557 
8558  /* The period needs to be clamped between minPeriodInFrames and maxPeriodInFrames. */
8559  actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames);
8560 
8561  #if defined(MA_DEBUG_OUTPUT)
8562  printf("[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames);
8563  printf(" defaultPeriodInFrames=%d\n", defaultPeriodInFrames);
8564  printf(" fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames);
8565  printf(" minPeriodInFrames=%d\n", minPeriodInFrames);
8566  printf(" maxPeriodInFrames=%d\n", maxPeriodInFrames);
8567  #endif
8568 
8569  /* If the client requested a largish buffer than we don't actually want to use low latency shared mode because it forces small buffers. */
8570  if (actualPeriodInFrames >= desiredPeriodInFrames) {
8571  /*
8572  MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified,
8573  IAudioClient3_InitializeSharedAudioStream() will fail.
8574  */
8575  hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (WAVEFORMATEX*)&wf, NULL);
8576  if (SUCCEEDED(hr)) {
8577  wasInitializedUsingIAudioClient3 = MA_TRUE;
8578  pData->periodSizeInFramesOut = actualPeriodInFrames;
8579  pData->bufferSizeInFramesOut = actualPeriodInFrames * pData->periodsOut;
8580  #if defined(MA_DEBUG_OUTPUT)
8581  printf("[WASAPI] Using IAudioClient3\n");
8582  printf(" periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut);
8583  printf(" bufferSizeInFramesOut=%d\n", pData->bufferSizeInFramesOut);
8584  #endif
8585  } else {
8586  #if defined(MA_DEBUG_OUTPUT)
8587  printf("[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n");
8588  #endif
8589  }
8590  } else {
8591  #if defined(MA_DEBUG_OUTPUT)
8592  printf("[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\n");
8593  #endif
8594  }
8595  } else {
8596  #if defined(MA_DEBUG_OUTPUT)
8597  printf("[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n");
8598  #endif
8599  }
8600 
8601  ma_IAudioClient3_Release(pAudioClient3);
8602  pAudioClient3 = NULL;
8603  }
8604  }
8605 #else
8606  #if defined(MA_DEBUG_OUTPUT)
8607  printf("[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n");
8608  #endif
8609 #endif
8610 
8611  /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */
8612  if (!wasInitializedUsingIAudioClient3) {
8613  MA_REFERENCE_TIME bufferDuration = bufferDurationInMicroseconds*10;
8614  hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL);
8615  if (FAILED(hr)) {
8616  if (hr == E_ACCESSDENIED) {
8617  errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED;
8618  } else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
8619  errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_DEVICE_BUSY;
8620  } else {
8621  errorMsg = "[WASAPI] Failed to initialize device.", result = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
8622  }
8623 
8624  goto done;
8625  }
8626  }
8627  }
8628 
8629  if (!wasInitializedUsingIAudioClient3) {
8630  hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &pData->bufferSizeInFramesOut);
8631  if (FAILED(hr)) {
8632  errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
8633  goto done;
8634  }
8635 
8636  pData->periodSizeInFramesOut = pData->bufferSizeInFramesOut / pData->periodsOut;
8637  }
8638 
8639  pData->usingAudioClient3 = wasInitializedUsingIAudioClient3;
8640 
8641  if (deviceType == ma_device_type_playback) {
8642  hr = ma_IAudioClient_GetService((ma_IAudioClient*)pData->pAudioClient, &MA_IID_IAudioRenderClient, (void**)&pData->pRenderClient);
8643  } else {
8644  hr = ma_IAudioClient_GetService((ma_IAudioClient*)pData->pAudioClient, &MA_IID_IAudioCaptureClient, (void**)&pData->pCaptureClient);
8645  }
8646 
8647  if (FAILED(hr)) {
8648  errorMsg = "[WASAPI] Failed to get audio client service.", result = MA_API_NOT_FOUND;
8649  goto done;
8650  }
8651 
8652 
8653  /* Grab the name of the device. */
8654 #ifdef MA_WIN32_DESKTOP
8655  {
8656  ma_IPropertyStore *pProperties;
8657  hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties);
8658  if (SUCCEEDED(hr)) {
8659  PROPVARIANT varName;
8660  ma_PropVariantInit(&varName);
8661  hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName);
8662  if (SUCCEEDED(hr)) {
8663  WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE);
8664  ma_PropVariantClear(pContext, &varName);
8665  }
8666 
8667  ma_IPropertyStore_Release(pProperties);
8668  }
8669  }
8670 #endif
8671 
8672 done:
8673  /* Clean up. */
8674 #ifdef MA_WIN32_DESKTOP
8675  if (pDeviceInterface != NULL) {
8676  ma_IMMDevice_Release(pDeviceInterface);
8677  }
8678 #else
8679  if (pDeviceInterface != NULL) {
8680  ma_IUnknown_Release(pDeviceInterface);
8681  }
8682 #endif
8683 
8684  if (result != MA_SUCCESS) {
8685  if (pData->pRenderClient) {
8686  ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient);
8687  pData->pRenderClient = NULL;
8688  }
8689  if (pData->pCaptureClient) {
8690  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient);
8691  pData->pCaptureClient = NULL;
8692  }
8693  if (pData->pAudioClient) {
8694  ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
8695  pData->pAudioClient = NULL;
8696  }
8697 
8698  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, errorMsg, result);
8699  } else {
8700  return MA_SUCCESS;
8701  }
8702 }
8703 
8704 ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType)
8705 {
8706  ma_device_init_internal_data__wasapi data;
8707  ma_result result;
8708 
8709  ma_assert(pDevice != NULL);
8710 
8711  /* We only re-initialize the playback or capture device. Never a full-duplex device. */
8712  if (deviceType == ma_device_type_duplex) {
8713  return MA_INVALID_ARGS;
8714  }
8715 
8716  if (deviceType == ma_device_type_playback) {
8717  data.formatIn = pDevice->playback.format;
8718  data.channelsIn = pDevice->playback.channels;
8719  ma_copy_memory(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
8720  data.shareMode = pDevice->playback.shareMode;
8721  data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
8722  data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
8723  data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
8724  } else {
8725  data.formatIn = pDevice->capture.format;
8726  data.channelsIn = pDevice->capture.channels;
8727  ma_copy_memory(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
8728  data.shareMode = pDevice->capture.shareMode;
8729  data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
8730  data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
8731  data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
8732  }
8733 
8734  data.sampleRateIn = pDevice->sampleRate;
8735  data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
8736  data.bufferSizeInFramesIn = pDevice->wasapi.originalBufferSizeInFrames;
8737  data.bufferSizeInMillisecondsIn = pDevice->wasapi.originalBufferSizeInMilliseconds;
8738  data.periodsIn = pDevice->wasapi.originalPeriods;
8739  data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC;
8740  data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC;
8741  result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data);
8742  if (result != MA_SUCCESS) {
8743  return result;
8744  }
8745 
8746  /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */
8747  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
8748  if (pDevice->wasapi.pCaptureClient) {
8749  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
8750  pDevice->wasapi.pCaptureClient = NULL;
8751  }
8752 
8753  if (pDevice->wasapi.pAudioClientCapture) {
8754  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
8755  pDevice->wasapi.pAudioClientCapture = NULL;
8756  }
8757 
8758  pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
8759  pDevice->wasapi.pCaptureClient = data.pCaptureClient;
8760 
8761  pDevice->capture.internalFormat = data.formatOut;
8762  pDevice->capture.internalChannels = data.channelsOut;
8763  pDevice->capture.internalSampleRate = data.sampleRateOut;
8764  ma_copy_memory(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
8765  pDevice->capture.internalBufferSizeInFrames = data.bufferSizeInFramesOut;
8766  pDevice->capture.internalPeriods = data.periodsOut;
8767  ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);
8768 
8769  ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture);
8770 
8771  pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
8772  ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
8773 
8774  /* The device may be in a started state. If so we need to immediately restart it. */
8775  if (pDevice->wasapi.isStartedCapture) {
8776  HRESULT hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
8777  if (FAILED(hr)) {
8778  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device after reinitialization.", MA_FAILED_TO_START_BACKEND_DEVICE);
8779  }
8780  }
8781  }
8782 
8783  if (deviceType == ma_device_type_playback) {
8784  if (pDevice->wasapi.pRenderClient) {
8785  ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
8786  pDevice->wasapi.pRenderClient = NULL;
8787  }
8788 
8789  if (pDevice->wasapi.pAudioClientPlayback) {
8790  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
8791  pDevice->wasapi.pAudioClientPlayback = NULL;
8792  }
8793 
8794  pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
8795  pDevice->wasapi.pRenderClient = data.pRenderClient;
8796 
8797  pDevice->playback.internalFormat = data.formatOut;
8798  pDevice->playback.internalChannels = data.channelsOut;
8799  pDevice->playback.internalSampleRate = data.sampleRateOut;
8800  ma_copy_memory(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
8801  pDevice->playback.internalBufferSizeInFrames = data.bufferSizeInFramesOut;
8802  pDevice->playback.internalPeriods = data.periodsOut;
8803  ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);
8804 
8805  ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback);
8806 
8807  pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
8808  ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
8809 
8810  /* The device may be in a started state. If so we need to immediately restart it. */
8811  if (pDevice->wasapi.isStartedPlayback) {
8812  HRESULT hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
8813  if (FAILED(hr)) {
8814  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device after reinitialization.", MA_FAILED_TO_START_BACKEND_DEVICE);
8815  }
8816  }
8817  }
8818 
8819  return MA_SUCCESS;
8820 }
8821 
8822 ma_result ma_device_init__wasapi(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
8823 {
8825 
8826  (void)pContext;
8827 
8828  ma_assert(pContext != NULL);
8829  ma_assert(pDevice != NULL);
8830 
8831  ma_zero_object(&pDevice->wasapi);
8832  pDevice->wasapi.originalBufferSizeInFrames = pConfig->bufferSizeInFrames;
8833  pDevice->wasapi.originalBufferSizeInMilliseconds = pConfig->bufferSizeInMilliseconds;
8834  pDevice->wasapi.originalPeriods = pConfig->periods;
8835  pDevice->wasapi.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC;
8836  pDevice->wasapi.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC;
8837 
8838  /* Exclusive mode is not allowed with loopback. */
8840  return MA_INVALID_DEVICE_CONFIG;
8841  }
8842 
8844  ma_device_init_internal_data__wasapi data;
8845  data.formatIn = pConfig->capture.format;
8846  data.channelsIn = pConfig->capture.channels;
8847  data.sampleRateIn = pConfig->sampleRate;
8848  ma_copy_memory(data.channelMapIn, pConfig->capture.channelMap, sizeof(pConfig->capture.channelMap));
8849  data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
8850  data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
8851  data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
8852  data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
8853  data.shareMode = pConfig->capture.shareMode;
8854  data.bufferSizeInFramesIn = pConfig->bufferSizeInFrames;
8855  data.bufferSizeInMillisecondsIn = pConfig->bufferSizeInMilliseconds;
8856  data.periodsIn = pConfig->periods;
8857  data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
8858  data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
8859 
8860  result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pConfig->capture.pDeviceID, &data);
8861  if (result != MA_SUCCESS) {
8862  return result;
8863  }
8864 
8865  pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
8866  pDevice->wasapi.pCaptureClient = data.pCaptureClient;
8867 
8868  pDevice->capture.internalFormat = data.formatOut;
8869  pDevice->capture.internalChannels = data.channelsOut;
8870  pDevice->capture.internalSampleRate = data.sampleRateOut;
8871  ma_copy_memory(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
8872  pDevice->capture.internalBufferSizeInFrames = data.bufferSizeInFramesOut;
8873  pDevice->capture.internalPeriods = data.periodsOut;
8874  ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);
8875 
8876  /*
8877  The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled,
8878  however, because we want to block until we actually have something for the first call to ma_device_read().
8879  */
8880  pDevice->wasapi.hEventCapture = CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */
8881  if (pDevice->wasapi.hEventCapture == NULL) {
8882  if (pDevice->wasapi.pCaptureClient != NULL) {
8883  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
8884  pDevice->wasapi.pCaptureClient = NULL;
8885  }
8886  if (pDevice->wasapi.pAudioClientCapture != NULL) {
8887  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
8888  pDevice->wasapi.pAudioClientCapture = NULL;
8889  }
8890 
8891  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture.", MA_FAILED_TO_CREATE_EVENT);
8892  }
8893  ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture);
8894 
8895  pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
8896  ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
8897  }
8898 
8899  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
8900  ma_device_init_internal_data__wasapi data;
8901  data.formatIn = pConfig->playback.format;
8902  data.channelsIn = pConfig->playback.channels;
8903  data.sampleRateIn = pConfig->sampleRate;
8904  ma_copy_memory(data.channelMapIn, pConfig->playback.channelMap, sizeof(pConfig->playback.channelMap));
8905  data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
8906  data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
8907  data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
8908  data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
8909  data.shareMode = pConfig->playback.shareMode;
8910  data.bufferSizeInFramesIn = pConfig->bufferSizeInFrames;
8911  data.bufferSizeInMillisecondsIn = pConfig->bufferSizeInMilliseconds;
8912  data.periodsIn = pConfig->periods;
8913  data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
8914  data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
8915 
8916  result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pConfig->playback.pDeviceID, &data);
8917  if (result != MA_SUCCESS) {
8918  if (pConfig->deviceType == ma_device_type_duplex) {
8919  if (pDevice->wasapi.pCaptureClient != NULL) {
8920  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
8921  pDevice->wasapi.pCaptureClient = NULL;
8922  }
8923  if (pDevice->wasapi.pAudioClientCapture != NULL) {
8924  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
8925  pDevice->wasapi.pAudioClientCapture = NULL;
8926  }
8927 
8928  CloseHandle(pDevice->wasapi.hEventCapture);
8929  pDevice->wasapi.hEventCapture = NULL;
8930  }
8931  return result;
8932  }
8933 
8934  pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
8935  pDevice->wasapi.pRenderClient = data.pRenderClient;
8936 
8937  pDevice->playback.internalFormat = data.formatOut;
8938  pDevice->playback.internalChannels = data.channelsOut;
8939  pDevice->playback.internalSampleRate = data.sampleRateOut;
8940  ma_copy_memory(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
8941  pDevice->playback.internalBufferSizeInFrames = data.bufferSizeInFramesOut;
8942  pDevice->playback.internalPeriods = data.periodsOut;
8943  ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);
8944 
8945  /*
8946  The event for playback is needs to be manual reset because we want to explicitly control the fact that it becomes signalled
8947  only after the whole available space has been filled, never before.
8948 
8949  The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able
8950  to get passed WaitForMultipleObjects().
8951  */
8952  pDevice->wasapi.hEventPlayback = CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */
8953  if (pDevice->wasapi.hEventPlayback == NULL) {
8954  if (pConfig->deviceType == ma_device_type_duplex) {
8955  if (pDevice->wasapi.pCaptureClient != NULL) {
8956  ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
8957  pDevice->wasapi.pCaptureClient = NULL;
8958  }
8959  if (pDevice->wasapi.pAudioClientCapture != NULL) {
8960  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
8961  pDevice->wasapi.pAudioClientCapture = NULL;
8962  }
8963 
8964  CloseHandle(pDevice->wasapi.hEventCapture);
8965  pDevice->wasapi.hEventCapture = NULL;
8966  }
8967 
8968  if (pDevice->wasapi.pRenderClient != NULL) {
8969  ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
8970  pDevice->wasapi.pRenderClient = NULL;
8971  }
8972  if (pDevice->wasapi.pAudioClientPlayback != NULL) {
8973  ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
8974  pDevice->wasapi.pAudioClientPlayback = NULL;
8975  }
8976 
8977  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback.", MA_FAILED_TO_CREATE_EVENT);
8978  }
8979  ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback);
8980 
8981  pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
8982  ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
8983  }
8984 
8985  /*
8986  We need to get notifications of when the default device changes. We do this through a device enumerator by
8987  registering a IMMNotificationClient with it. We only care about this if it's the default device.
8988  */
8989 #ifdef MA_WIN32_DESKTOP
8990  {
8991  ma_IMMDeviceEnumerator* pDeviceEnumerator;
8992  HRESULT hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
8993  if (FAILED(hr)) {
8994  ma_device_uninit__wasapi(pDevice);
8995  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
8996  }
8997 
8998  pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl;
8999  pDevice->wasapi.notificationClient.counter = 1;
9000  pDevice->wasapi.notificationClient.pDevice = pDevice;
9001 
9002  hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient);
9003  if (SUCCEEDED(hr)) {
9004  pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator;
9005  } else {
9006  /* Not the end of the world if we fail to register the notification callback. We just won't support automatic stream routing. */
9007  ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
9008  }
9009  }
9010 #endif
9011 
9012  ma_atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE);
9013  ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
9014 
9015  return MA_SUCCESS;
9016 }
9017 
9018 ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount)
9019 {
9020  ma_uint32 paddingFramesCount;
9021  HRESULT hr;
9022  ma_share_mode shareMode;
9023 
9024  ma_assert(pDevice != NULL);
9025  ma_assert(pFrameCount != NULL);
9026 
9027  *pFrameCount = 0;
9028 
9029  if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) {
9030  return MA_INVALID_OPERATION;
9031  }
9032 
9033  hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount);
9034  if (FAILED(hr)) {
9035  return MA_DEVICE_UNAVAILABLE;
9036  }
9037 
9038  /* Slightly different rules for exclusive and shared modes. */
9039  shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode;
9040  if (shareMode == ma_share_mode_exclusive) {
9041  *pFrameCount = paddingFramesCount;
9042  } else {
9043  if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
9044  *pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount;
9045  } else {
9046  *pFrameCount = paddingFramesCount;
9047  }
9048  }
9049 
9050  return MA_SUCCESS;
9051 }
9052 
9053 ma_bool32 ma_device_is_reroute_required__wasapi(ma_device* pDevice, ma_device_type deviceType)
9054 {
9055  ma_assert(pDevice != NULL);
9056 
9057  if (deviceType == ma_device_type_playback) {
9058  return pDevice->wasapi.hasDefaultPlaybackDeviceChanged;
9059  }
9060 
9061  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
9062  return pDevice->wasapi.hasDefaultCaptureDeviceChanged;
9063  }
9064 
9065  return MA_FALSE;
9066 }
9067 
9068 ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType)
9069 {
9070  ma_result result;
9071 
9072  if (deviceType == ma_device_type_duplex) {
9073  return MA_INVALID_ARGS;
9074  }
9075 
9076  if (deviceType == ma_device_type_playback) {
9077  ma_atomic_exchange_32(&pDevice->wasapi.hasDefaultPlaybackDeviceChanged, MA_FALSE);
9078  }
9079  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
9080  ma_atomic_exchange_32(&pDevice->wasapi.hasDefaultCaptureDeviceChanged, MA_FALSE);
9081  }
9082 
9083 
9084  #ifdef MA_DEBUG_OUTPUT
9085  printf("=== CHANGING DEVICE ===\n");
9086  #endif
9087 
9088  result = ma_device_reinit__wasapi(pDevice, deviceType);
9089  if (result != MA_SUCCESS) {
9090  return result;
9091  }
9092 
9093  ma_device__post_init_setup(pDevice, deviceType);
9094 
9095  return MA_SUCCESS;
9096 }
9097 
9098 
9099 ma_result ma_device_stop__wasapi(ma_device* pDevice)
9100 {
9101  ma_assert(pDevice != NULL);
9102 
9103  /*
9104  We need to explicitly signal the capture event in loopback mode to ensure we return from WaitForSingleObject() when nothing is being played. When nothing
9105  is being played, the event is never signalled internally by WASAPI which means we will deadlock when stopping the device.
9106  */
9107  if (pDevice->type == ma_device_type_loopback) {
9108  SetEvent((HANDLE)pDevice->wasapi.hEventCapture);
9109  }
9110 
9111  return MA_SUCCESS;
9112 }
9113 
9114 
9115 ma_result ma_device_main_loop__wasapi(ma_device* pDevice)
9116 {
9117  ma_result result;
9118  HRESULT hr;
9119  ma_bool32 exitLoop = MA_FALSE;
9120  ma_uint32 framesWrittenToPlaybackDevice = 0;
9121  ma_uint32 mappedBufferSizeInFramesCapture = 0;
9122  ma_uint32 mappedBufferSizeInFramesPlayback = 0;
9123  ma_uint32 mappedBufferFramesRemainingCapture = 0;
9124  ma_uint32 mappedBufferFramesRemainingPlayback = 0;
9125  BYTE* pMappedBufferCapture = NULL;
9126  BYTE* pMappedBufferPlayback = NULL;
9127  ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
9128  ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
9129  ma_uint8 inputDataInExternalFormat[4096];
9130  ma_uint32 inputDataInExternalFormatCap = sizeof(inputDataInExternalFormat) / bpfCapture;
9131  ma_uint8 outputDataInExternalFormat[4096];
9132  ma_uint32 outputDataInExternalFormatCap = sizeof(outputDataInExternalFormat) / bpfPlayback;
9133  ma_uint32 periodSizeInFramesCapture = 0;
9134 
9135  ma_assert(pDevice != NULL);
9136 
9137  /* The capture device needs to be started immediately. */
9138  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
9139  periodSizeInFramesCapture = (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods);
9140 
9141  hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
9142  if (FAILED(hr)) {
9143  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device.", MA_FAILED_TO_START_BACKEND_DEVICE);
9144  }
9145  ma_atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_TRUE);
9146  }
9147 
9148  while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
9149  /* We may need to reroute the device. */
9150  if (ma_device_is_reroute_required__wasapi(pDevice, ma_device_type_playback)) {
9151  result = ma_device_reroute__wasapi(pDevice, ma_device_type_playback);
9152  if (result != MA_SUCCESS) {
9153  exitLoop = MA_TRUE;
9154  break;
9155  }
9156  }
9157  if (ma_device_is_reroute_required__wasapi(pDevice, ma_device_type_capture)) {
9158  result = ma_device_reroute__wasapi(pDevice, (pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
9159  if (result != MA_SUCCESS) {
9160  exitLoop = MA_TRUE;
9161  break;
9162  }
9163  }
9164 
9165  switch (pDevice->type)
9166  {
9167  case ma_device_type_duplex:
9168  {
9169  ma_uint32 framesAvailableCapture;
9170  ma_uint32 framesAvailablePlayback;
9171  DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */
9172 
9173  /* The process is to map the playback buffer and fill it as quickly as possible from input data. */
9174  if (pMappedBufferPlayback == NULL) {
9175  /* WASAPI is weird with exclusive mode. You need to wait on the event _before_ querying the available frames. */
9176  if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
9177  if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) {
9178  return MA_ERROR; /* Wait failed. */
9179  }
9180  }
9181 
9182  result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
9183  if (result != MA_SUCCESS) {
9184  return result;
9185  }
9186 
9187  /*printf("TRACE 1: framesAvailablePlayback=%d\n", framesAvailablePlayback);*/
9188 
9189 
9190  /* In exclusive mode, the frame count needs to exactly match the value returned by GetCurrentPadding(). */
9191  if (pDevice->playback.shareMode != ma_share_mode_exclusive) {
9192  if (framesAvailablePlayback > pDevice->wasapi.periodSizeInFramesPlayback) {
9193  framesAvailablePlayback = pDevice->wasapi.periodSizeInFramesPlayback;
9194  }
9195  }
9196 
9197  /* If there's no frames available in the playback device we need to wait for more. */
9198  if (framesAvailablePlayback == 0) {
9199  /* In exclusive mode we waited at the top. */
9200  if (pDevice->playback.shareMode != ma_share_mode_exclusive) {
9201  if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) {
9202  return MA_ERROR; /* Wait failed. */
9203  }
9204  }
9205 
9206  continue;
9207  }
9208 
9209  /* We're ready to map the playback device's buffer. We don't release this until it's been entirely filled. */
9210  hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedBufferPlayback);
9211  if (FAILED(hr)) {
9212  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
9213  exitLoop = MA_TRUE;
9214  break;
9215  }
9216 
9217  mappedBufferSizeInFramesPlayback = framesAvailablePlayback;
9218  mappedBufferFramesRemainingPlayback = framesAvailablePlayback;
9219  }
9220 
9221  /* At this point we should have a buffer available for output. We need to keep writing input samples to it. */
9222  for (;;) {
9223  /* Try grabbing some captured data if we haven't already got a mapped buffer. */
9224  if (pMappedBufferCapture == NULL) {
9225  if (pDevice->capture.shareMode == ma_share_mode_shared) {
9226  if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) {
9227  return MA_ERROR; /* Wait failed. */
9228  }
9229  }
9230 
9231  result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
9232  if (result != MA_SUCCESS) {
9233  exitLoop = MA_TRUE;
9234  break;
9235  }
9236 
9237  /*printf("TRACE 2: framesAvailableCapture=%d\n", framesAvailableCapture);*/
9238 
9239  /* Wait for more if nothing is available. */
9240  if (framesAvailableCapture == 0) {
9241  /* In exclusive mode we waited at the top. */
9242  if (pDevice->capture.shareMode != ma_share_mode_shared) {
9243  if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) {
9244  return MA_ERROR; /* Wait failed. */
9245  }
9246  }
9247 
9248  continue;
9249  }
9250 
9251  /* Getting here means there's data available for writing to the output device. */
9252  mappedBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
9253  hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedBufferCapture, &mappedBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
9254  if (FAILED(hr)) {
9255  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
9256  exitLoop = MA_TRUE;
9257  break;
9258  }
9259 
9260 
9261  /* Overrun detection. */
9262  if ((flagsCapture & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
9263  /* Glitched. Probably due to an overrun. */
9264  #ifdef MA_DEBUG_OUTPUT
9265  printf("[WASAPI] Data discontinuity (possible overrun). framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedBufferSizeInFramesCapture);
9266  #endif
9267 
9268  /*
9269  Exeriment: If we get an overrun it probably means we're straddling the end of the buffer. In order to prevent a never-ending sequence of glitches let's experiment
9270  by dropping every frame until we're left with only a single period. To do this we just keep retrieving and immediately releasing buffers until we're down to the
9271  last period.
9272  */
9273  if (framesAvailableCapture >= pDevice->wasapi.actualBufferSizeInFramesCapture /*(pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods)*/) {
9274  #ifdef MA_DEBUG_OUTPUT
9275  printf("[WASAPI] Synchronizing capture stream. ");
9276  #endif
9277  do
9278  {
9279  hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedBufferSizeInFramesCapture);
9280  if (FAILED(hr)) {
9281  break;
9282  }
9283 
9284  framesAvailableCapture -= mappedBufferSizeInFramesCapture;
9285 
9286  if (framesAvailableCapture > 0) {
9287  mappedBufferSizeInFramesCapture = ma_min(framesAvailableCapture, periodSizeInFramesCapture);
9288  hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedBufferCapture, &mappedBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
9289  if (FAILED(hr)) {
9290  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
9291  exitLoop = MA_TRUE;
9292  break;
9293  }
9294  } else {
9295  pMappedBufferCapture = NULL;
9296  mappedBufferSizeInFramesCapture = 0;
9297  }
9298  } while (framesAvailableCapture > periodSizeInFramesCapture);
9299  #ifdef MA_DEBUG_OUTPUT
9300  printf("framesAvailableCapture=%d, mappedBufferSizeInFramesCapture=%d\n", framesAvailableCapture, mappedBufferSizeInFramesCapture);
9301  #endif
9302  }
9303  } else {
9304  #ifdef MA_DEBUG_OUTPUT
9305  if (flagsCapture != 0) {
9306  printf("[WASAPI] Capture Flags: %d\n", flagsCapture);
9307  }
9308  #endif
9309  }
9310 
9311  mappedBufferFramesRemainingCapture = mappedBufferSizeInFramesCapture;
9312 
9313  pDevice->capture._dspFrameCount = mappedBufferSizeInFramesCapture;
9314  if ((flagsCapture & MA_AUDCLNT_BUFFERFLAGS_SILENT) == 0) {
9315  pDevice->capture._dspFrames = (const ma_uint8*)pMappedBufferCapture;
9316  } else {
9317  pDevice->capture._dspFrames = NULL;
9318  }
9319  }
9320 
9321 
9322  /* At this point we should have both input and output data available. We now need to convert the data and post it to the client. */
9323  for (;;) {
9324  BYTE* pRunningBufferCapture;
9325  BYTE* pRunningBufferPlayback;
9326  ma_uint32 framesToProcess;
9327  ma_uint32 framesProcessed;
9328 
9329  pRunningBufferCapture = pMappedBufferCapture + ((mappedBufferSizeInFramesCapture - mappedBufferFramesRemainingCapture ) * bpfPlayback);
9330  pRunningBufferPlayback = pMappedBufferPlayback + ((mappedBufferSizeInFramesPlayback - mappedBufferFramesRemainingPlayback) * bpfPlayback);
9331 
9332  /* There may be some data sitting in the converter that needs to be processed first. Once this is exhaused, run the data callback again. */
9333  if (!pDevice->playback.converter.isPassthrough) {
9334  framesProcessed = (ma_uint32)ma_pcm_converter_read(&pDevice->playback.converter, pRunningBufferPlayback, mappedBufferFramesRemainingPlayback);
9335  if (framesProcessed > 0) {
9336  mappedBufferFramesRemainingPlayback -= framesProcessed;
9337  if (mappedBufferFramesRemainingPlayback == 0) {
9338  break;
9339  }
9340  }
9341  }
9342 
9343  /*
9344  Getting here means we need to fire the callback. If format conversion is unnecessary, we can optimize this by passing the pointers to the internal
9345  buffers directly to the callback.
9346  */
9347  if (pDevice->capture.converter.isPassthrough && pDevice->playback.converter.isPassthrough) {
9348  /* Optimal path. We can pass mapped pointers directly to the callback. */
9349  framesToProcess = ma_min(mappedBufferFramesRemainingCapture, mappedBufferFramesRemainingPlayback);
9350  framesProcessed = framesToProcess;
9351 
9352  ma_device__on_data(pDevice, pRunningBufferPlayback, pRunningBufferCapture, framesToProcess);
9353 
9354  mappedBufferFramesRemainingCapture -= framesProcessed;
9355  mappedBufferFramesRemainingPlayback -= framesProcessed;
9356 
9357  if (mappedBufferFramesRemainingCapture == 0) {
9358  break; /* Exhausted input data. */
9359  }
9360  if (mappedBufferFramesRemainingPlayback == 0) {
9361  break; /* Exhausted output data. */
9362  }
9363  } else if (pDevice->capture.converter.isPassthrough) {
9364  /* The input buffer is a passthrough, but the playback buffer requires a conversion. */
9365  framesToProcess = ma_min(mappedBufferFramesRemainingCapture, outputDataInExternalFormatCap);
9366  framesProcessed = framesToProcess;
9367 
9368  ma_device__on_data(pDevice, outputDataInExternalFormat, pRunningBufferCapture, framesToProcess);
9369  mappedBufferFramesRemainingCapture -= framesProcessed;
9370 
9371  pDevice->playback._dspFrameCount = framesProcessed;
9372  pDevice->playback._dspFrames = (const ma_uint8*)outputDataInExternalFormat;
9373 
9374  if (mappedBufferFramesRemainingCapture == 0) {
9375  break; /* Exhausted input data. */
9376  }
9377  } else if (pDevice->playback.converter.isPassthrough) {
9378  /* The input buffer requires conversion, the playback buffer is passthrough. */
9379  framesToProcess = ma_min(inputDataInExternalFormatCap, mappedBufferFramesRemainingPlayback);
9380  framesProcessed = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, inputDataInExternalFormat, framesToProcess);
9381  if (framesProcessed == 0) {
9382  /* Getting here means we've run out of input data. */
9383  mappedBufferFramesRemainingCapture = 0;
9384  break;
9385  }
9386 
9387  ma_device__on_data(pDevice, pRunningBufferPlayback, inputDataInExternalFormat, framesProcessed);
9388  mappedBufferFramesRemainingPlayback -= framesProcessed;
9389 
9390  if (framesProcessed < framesToProcess) {
9391  mappedBufferFramesRemainingCapture = 0;
9392  break; /* Exhausted input data. */
9393  }
9394 
9395  if (mappedBufferFramesRemainingPlayback == 0) {
9396  break; /* Exhausted output data. */
9397  }
9398  } else {
9399  framesToProcess = ma_min(inputDataInExternalFormatCap, outputDataInExternalFormatCap);
9400  framesProcessed = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, inputDataInExternalFormat, framesToProcess);
9401  if (framesProcessed == 0) {
9402  /* Getting here means we've run out of input data. */
9403  mappedBufferFramesRemainingCapture = 0;
9404  break;
9405  }
9406 
9407  ma_device__on_data(pDevice, outputDataInExternalFormat, inputDataInExternalFormat, framesProcessed);
9408 
9409  pDevice->playback._dspFrameCount = framesProcessed;
9410  pDevice->playback._dspFrames = (const ma_uint8*)outputDataInExternalFormat;
9411 
9412  if (framesProcessed < framesToProcess) {
9413  /* Getting here means we've run out of input data. */
9414  mappedBufferFramesRemainingCapture = 0;
9415  break;
9416  }
9417  }
9418  }
9419 
9420 
9421  /* If at this point we've run out of capture data we need to release the buffer. */
9422  if (mappedBufferFramesRemainingCapture == 0 && pMappedBufferCapture != NULL) {
9423  hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedBufferSizeInFramesCapture);
9424  if (FAILED(hr)) {
9425  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from capture device after reading from the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
9426  exitLoop = MA_TRUE;
9427  break;
9428  }
9429 
9430  /*printf("TRACE: Released capture buffer\n");*/
9431 
9432  pMappedBufferCapture = NULL;
9433  mappedBufferFramesRemainingCapture = 0;
9434  mappedBufferSizeInFramesCapture = 0;
9435  }
9436 
9437  /* Get out of this loop if we're run out of room in the playback buffer. */
9438  if (mappedBufferFramesRemainingPlayback == 0) {
9439  break;
9440  }
9441  }
9442 
9443 
9444  /* If at this point we've run out of data we need to release the buffer. */
9445  if (mappedBufferFramesRemainingPlayback == 0 && pMappedBufferPlayback != NULL) {
9446  hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, mappedBufferSizeInFramesPlayback, 0);
9447  if (FAILED(hr)) {
9448  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from playback device after writing to the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
9449  exitLoop = MA_TRUE;
9450  break;
9451  }
9452 
9453  /*printf("TRACE: Released playback buffer\n");*/
9454  framesWrittenToPlaybackDevice += mappedBufferSizeInFramesPlayback;
9455 
9456  pMappedBufferPlayback = NULL;
9457  mappedBufferFramesRemainingPlayback = 0;
9458  mappedBufferSizeInFramesPlayback = 0;
9459  }
9460 
9461  if (!pDevice->wasapi.isStartedPlayback) {
9462  ma_uint32 startThreshold = pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods * 1;
9463 
9464  /* Prevent a deadlock. If we don't clamp against the actual buffer size we'll never end up starting the playback device which will result in a deadlock. */
9465  if (startThreshold > pDevice->wasapi.actualBufferSizeInFramesPlayback) {
9466  startThreshold = pDevice->wasapi.actualBufferSizeInFramesPlayback;
9467  }
9468 
9469  if (pDevice->playback.shareMode == ma_share_mode_exclusive || framesWrittenToPlaybackDevice >= startThreshold) {
9470  hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
9471  if (FAILED(hr)) {
9472  ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
9473  ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
9474  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", MA_FAILED_TO_START_BACKEND_DEVICE);
9475  }
9476  ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
9477  }
9478  }
9479  } break;
9480 
9481 
9482 
9485  {
9486  ma_uint32 framesAvailableCapture;
9487  DWORD flagsCapture; /* Passed to IAudioCaptureClient_GetBuffer(). */
9488 
9489  /* Wait for data to become available first. */
9490  if (WaitForSingleObject(pDevice->wasapi.hEventCapture, INFINITE) == WAIT_FAILED) {
9491  exitLoop = MA_TRUE;
9492  break; /* Wait failed. */
9493  }
9494 
9495  /* See how many frames are available. Since we waited at the top, I don't think this should ever return 0. I'm checking for this anyway. */
9496  result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &framesAvailableCapture);
9497  if (result != MA_SUCCESS) {
9498  exitLoop = MA_TRUE;
9499  break;
9500  }
9501 
9502  if (framesAvailableCapture < pDevice->wasapi.periodSizeInFramesCapture) {
9503  continue; /* Nothing available. Keep waiting. */
9504  }
9505 
9506  /* Map the data buffer in preparation for sending to the client. */
9507  mappedBufferSizeInFramesCapture = framesAvailableCapture;
9508  hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pMappedBufferCapture, &mappedBufferSizeInFramesCapture, &flagsCapture, NULL, NULL);
9509  if (FAILED(hr)) {
9510  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
9511  exitLoop = MA_TRUE;
9512  break;
9513  }
9514 
9515  /* We should have a buffer at this point. */
9516  ma_device__send_frames_to_client(pDevice, mappedBufferSizeInFramesCapture, pMappedBufferCapture);
9517 
9518  /* At this point we're done with the buffer. */
9519  hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedBufferSizeInFramesCapture);
9520  pMappedBufferCapture = NULL; /* <-- Important. Not doing this can result in an error once we leave this loop because it will use this to know whether or not a final ReleaseBuffer() needs to be called. */
9521  mappedBufferSizeInFramesCapture = 0;
9522  if (FAILED(hr)) {
9523  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from capture device after reading from the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
9524  exitLoop = MA_TRUE;
9525  break;
9526  }
9527  } break;
9528 
9529 
9530 
9532  {
9533  ma_uint32 framesAvailablePlayback;
9534 
9535  /* Wait for space to become available first. */
9536  if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE) == WAIT_FAILED) {
9537  exitLoop = MA_TRUE;
9538  break; /* Wait failed. */
9539  }
9540 
9541  /* Check how much space is available. If this returns 0 we just keep waiting. */
9542  result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
9543  if (result != MA_SUCCESS) {
9544  exitLoop = MA_TRUE;
9545  break;
9546  }
9547 
9548  if (framesAvailablePlayback < pDevice->wasapi.periodSizeInFramesPlayback) {
9549  continue; /* No space available. */
9550  }
9551 
9552  /* Map a the data buffer in preparation for the callback. */
9553  hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, &pMappedBufferPlayback);
9554  if (FAILED(hr)) {
9555  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
9556  exitLoop = MA_TRUE;
9557  break;
9558  }
9559 
9560  /* We should have a buffer at this point. */
9561  ma_device__read_frames_from_client(pDevice, framesAvailablePlayback, pMappedBufferPlayback);
9562 
9563  /* At this point we're done writing to the device and we just need to release the buffer. */
9564  hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, framesAvailablePlayback, 0);
9565  pMappedBufferPlayback = NULL; /* <-- Important. Not doing this can result in an error once we leave this loop because it will use this to know whether or not a final ReleaseBuffer() needs to be called. */
9566  mappedBufferSizeInFramesPlayback = 0;
9567 
9568  if (FAILED(hr)) {
9569  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to release internal buffer from playback device after writing to the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
9570  exitLoop = MA_TRUE;
9571  break;
9572  }
9573 
9574  framesWrittenToPlaybackDevice += framesAvailablePlayback;
9575  if (!pDevice->wasapi.isStartedPlayback) {
9576  if (pDevice->playback.shareMode == ma_share_mode_exclusive || framesWrittenToPlaybackDevice >= (pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)*1) {
9577  hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
9578  if (FAILED(hr)) {
9579  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device.", MA_FAILED_TO_START_BACKEND_DEVICE);
9580  exitLoop = MA_TRUE;
9581  break;
9582  }
9583  ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
9584  }
9585  }
9586  } break;
9587 
9588  default: return MA_INVALID_ARGS;
9589  }
9590  }
9591 
9592  /* Here is where the device needs to be stopped. */
9593  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
9594  /* Any mapped buffers need to be released. */
9595  if (pMappedBufferCapture != NULL) {
9596  hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, mappedBufferSizeInFramesCapture);
9597  }
9598 
9599  hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
9600  if (FAILED(hr)) {
9601  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
9602  }
9603 
9604  /* The audio client needs to be reset otherwise restarting will fail. */
9605  hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
9606  if (FAILED(hr)) {
9607  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
9608  }
9609 
9610  ma_atomic_exchange_32(&pDevice->wasapi.isStartedCapture, MA_FALSE);
9611  }
9612 
9613  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
9614  /* Any mapped buffers need to be released. */
9615  if (pMappedBufferPlayback != NULL) {
9616  hr = ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, mappedBufferSizeInFramesPlayback, 0);
9617  }
9618 
9619  /*
9620  The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to
9621  the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played.
9622  */
9623  if (pDevice->wasapi.isStartedPlayback) {
9624  if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
9625  WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE);
9626  } else {
9627  ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1;
9628  ma_uint32 framesAvailablePlayback;
9629  for (;;) {
9630  result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
9631  if (result != MA_SUCCESS) {
9632  break;
9633  }
9634 
9635  if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) {
9636  break;
9637  }
9638 
9639  /*
9640  Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames
9641  has not changed, get out of the loop. I don't think this should ever happen, but I think it's nice to have just in case.
9642  */
9643  if (framesAvailablePlayback == prevFramesAvaialablePlayback) {
9644  break;
9645  }
9646  prevFramesAvaialablePlayback = framesAvailablePlayback;
9647 
9648  WaitForSingleObject(pDevice->wasapi.hEventPlayback, INFINITE);
9649  ResetEvent(pDevice->wasapi.hEventPlayback); /* Manual reset. */
9650  }
9651  }
9652  }
9653 
9654  hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
9655  if (FAILED(hr)) {
9656  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
9657  }
9658 
9659  /* The audio client needs to be reset otherwise restarting will fail. */
9660  hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
9661  if (FAILED(hr)) {
9662  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
9663  }
9664 
9665  ma_atomic_exchange_32(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
9666  }
9667 
9668  return MA_SUCCESS;
9669 }
9670 
9671 ma_result ma_context_uninit__wasapi(ma_context* pContext)
9672 {
9673  ma_assert(pContext != NULL);
9674  ma_assert(pContext->backend == ma_backend_wasapi);
9675  (void)pContext;
9676 
9677  return MA_SUCCESS;
9678 }
9679 
9680 ma_result ma_context_init__wasapi(const ma_context_config* pConfig, ma_context* pContext)
9681 {
9683 
9684  ma_assert(pContext != NULL);
9685 
9686  (void)pConfig;
9687 
9688 #ifdef MA_WIN32_DESKTOP
9689  /*
9690  WASAPI is only supported in Vista SP1 and newer. The reason for SP1 and not the base version of Vista is that event-driven
9691  exclusive mode does not work until SP1.
9692 
9693  Unfortunately older compilers don't define these functions so we need to dynamically load them in order to avoid a lin error.
9694  */
9695  {
9696  ma_OSVERSIONINFOEXW osvi;
9697  ma_handle kernel32DLL;
9698  ma_PFNVerifyVersionInfoW _VerifyVersionInfoW;
9699  ma_PFNVerSetConditionMask _VerSetConditionMask;
9700 
9701  kernel32DLL = ma_dlopen(pContext, "kernel32.dll");
9702  if (kernel32DLL == NULL) {
9703  return MA_NO_BACKEND;
9704  }
9705 
9706  _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW)ma_dlsym(pContext, kernel32DLL, "VerifyVersionInfoW");
9707  _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(pContext, kernel32DLL, "VerSetConditionMask");
9708  if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) {
9709  ma_dlclose(pContext, kernel32DLL);
9710  return MA_NO_BACKEND;
9711  }
9712 
9713  ma_zero_object(&osvi);
9714  osvi.dwOSVersionInfoSize = sizeof(osvi);
9715  osvi.dwMajorVersion = HIBYTE(MA_WIN32_WINNT_VISTA);
9716  osvi.dwMinorVersion = LOBYTE(MA_WIN32_WINNT_VISTA);
9717  osvi.wServicePackMajor = 1;
9718  if (_VerifyVersionInfoW(&osvi, MA_VER_MAJORVERSION | MA_VER_MINORVERSION | MA_VER_SERVICEPACKMAJOR, _VerSetConditionMask(_VerSetConditionMask(_VerSetConditionMask(0, MA_VER_MAJORVERSION, MA_VER_GREATER_EQUAL), MA_VER_MINORVERSION, MA_VER_GREATER_EQUAL), MA_VER_SERVICEPACKMAJOR, MA_VER_GREATER_EQUAL))) {
9719  result = MA_SUCCESS;
9720  } else {
9722  }
9723 
9724  ma_dlclose(pContext, kernel32DLL);
9725  }
9726 #endif
9727 
9728  if (result != MA_SUCCESS) {
9729  return result;
9730  }
9731 
9732  pContext->onUninit = ma_context_uninit__wasapi;
9733  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__wasapi;
9734  pContext->onEnumDevices = ma_context_enumerate_devices__wasapi;
9735  pContext->onGetDeviceInfo = ma_context_get_device_info__wasapi;
9736  pContext->onDeviceInit = ma_device_init__wasapi;
9737  pContext->onDeviceUninit = ma_device_uninit__wasapi;
9738  pContext->onDeviceStart = NULL; /* Not used. Started in onDeviceMainLoop. */
9739  pContext->onDeviceStop = ma_device_stop__wasapi; /* Required to ensure the capture event is signalled when stopping a loopback device while nothing is playing. */
9740  pContext->onDeviceMainLoop = ma_device_main_loop__wasapi;
9741 
9742  return result;
9743 }
9744 #endif
9745 
9746 /******************************************************************************
9747 
9748 DirectSound Backend
9749 
9750 ******************************************************************************/
9751 #ifdef MA_HAS_DSOUND
9752 /*#include <dsound.h>*/
9753 
9754 GUID MA_GUID_IID_DirectSoundNotify = {0xb0210783, 0x89cd, 0x11d0, {0xaf, 0x08, 0x00, 0xa0, 0xc9, 0x25, 0xcd, 0x16}};
9755 
9756 /* miniaudio only uses priority or exclusive modes. */
9757 #define MA_DSSCL_NORMAL 1
9758 #define MA_DSSCL_PRIORITY 2
9759 #define MA_DSSCL_EXCLUSIVE 3
9760 #define MA_DSSCL_WRITEPRIMARY 4
9761 
9762 #define MA_DSCAPS_PRIMARYMONO 0x00000001
9763 #define MA_DSCAPS_PRIMARYSTEREO 0x00000002
9764 #define MA_DSCAPS_PRIMARY8BIT 0x00000004
9765 #define MA_DSCAPS_PRIMARY16BIT 0x00000008
9766 #define MA_DSCAPS_CONTINUOUSRATE 0x00000010
9767 #define MA_DSCAPS_EMULDRIVER 0x00000020
9768 #define MA_DSCAPS_CERTIFIED 0x00000040
9769 #define MA_DSCAPS_SECONDARYMONO 0x00000100
9770 #define MA_DSCAPS_SECONDARYSTEREO 0x00000200
9771 #define MA_DSCAPS_SECONDARY8BIT 0x00000400
9772 #define MA_DSCAPS_SECONDARY16BIT 0x00000800
9773 
9774 #define MA_DSBCAPS_PRIMARYBUFFER 0x00000001
9775 #define MA_DSBCAPS_STATIC 0x00000002
9776 #define MA_DSBCAPS_LOCHARDWARE 0x00000004
9777 #define MA_DSBCAPS_LOCSOFTWARE 0x00000008
9778 #define MA_DSBCAPS_CTRL3D 0x00000010
9779 #define MA_DSBCAPS_CTRLFREQUENCY 0x00000020
9780 #define MA_DSBCAPS_CTRLPAN 0x00000040
9781 #define MA_DSBCAPS_CTRLVOLUME 0x00000080
9782 #define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100
9783 #define MA_DSBCAPS_CTRLFX 0x00000200
9784 #define MA_DSBCAPS_STICKYFOCUS 0x00004000
9785 #define MA_DSBCAPS_GLOBALFOCUS 0x00008000
9786 #define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000
9787 #define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000
9788 #define MA_DSBCAPS_LOCDEFER 0x00040000
9789 #define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000
9790 
9791 #define MA_DSBPLAY_LOOPING 0x00000001
9792 #define MA_DSBPLAY_LOCHARDWARE 0x00000002
9793 #define MA_DSBPLAY_LOCSOFTWARE 0x00000004
9794 #define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008
9795 #define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010
9796 #define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020
9797 
9798 #define MA_DSCBSTART_LOOPING 0x00000001
9799 
9800 typedef struct
9801 {
9802  DWORD dwSize;
9803  DWORD dwFlags;
9804  DWORD dwBufferBytes;
9805  DWORD dwReserved;
9806  WAVEFORMATEX* lpwfxFormat;
9807  GUID guid3DAlgorithm;
9808 } MA_DSBUFFERDESC;
9809 
9810 typedef struct
9811 {
9812  DWORD dwSize;
9813  DWORD dwFlags;
9814  DWORD dwBufferBytes;
9815  DWORD dwReserved;
9816  WAVEFORMATEX* lpwfxFormat;
9817  DWORD dwFXCount;
9818  void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */
9819 } MA_DSCBUFFERDESC;
9820 
9821 typedef struct
9822 {
9823  DWORD dwSize;
9824  DWORD dwFlags;
9825  DWORD dwMinSecondarySampleRate;
9826  DWORD dwMaxSecondarySampleRate;
9827  DWORD dwPrimaryBuffers;
9828  DWORD dwMaxHwMixingAllBuffers;
9829  DWORD dwMaxHwMixingStaticBuffers;
9830  DWORD dwMaxHwMixingStreamingBuffers;
9831  DWORD dwFreeHwMixingAllBuffers;
9832  DWORD dwFreeHwMixingStaticBuffers;
9833  DWORD dwFreeHwMixingStreamingBuffers;
9834  DWORD dwMaxHw3DAllBuffers;
9835  DWORD dwMaxHw3DStaticBuffers;
9836  DWORD dwMaxHw3DStreamingBuffers;
9837  DWORD dwFreeHw3DAllBuffers;
9838  DWORD dwFreeHw3DStaticBuffers;
9839  DWORD dwFreeHw3DStreamingBuffers;
9840  DWORD dwTotalHwMemBytes;
9841  DWORD dwFreeHwMemBytes;
9842  DWORD dwMaxContigFreeHwMemBytes;
9843  DWORD dwUnlockTransferRateHwBuffers;
9844  DWORD dwPlayCpuOverheadSwBuffers;
9845  DWORD dwReserved1;
9846  DWORD dwReserved2;
9847 } MA_DSCAPS;
9848 
9849 typedef struct
9850 {
9851  DWORD dwSize;
9852  DWORD dwFlags;
9853  DWORD dwBufferBytes;
9854  DWORD dwUnlockTransferRate;
9855  DWORD dwPlayCpuOverhead;
9856 } MA_DSBCAPS;
9857 
9858 typedef struct
9859 {
9860  DWORD dwSize;
9861  DWORD dwFlags;
9862  DWORD dwFormats;
9863  DWORD dwChannels;
9864 } MA_DSCCAPS;
9865 
9866 typedef struct
9867 {
9868  DWORD dwSize;
9869  DWORD dwFlags;
9870  DWORD dwBufferBytes;
9871  DWORD dwReserved;
9872 } MA_DSCBCAPS;
9873 
9874 typedef struct
9875 {
9876  DWORD dwOffset;
9877  HANDLE hEventNotify;
9878 } MA_DSBPOSITIONNOTIFY;
9879 
9880 typedef struct ma_IDirectSound ma_IDirectSound;
9881 typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer;
9882 typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture;
9883 typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer;
9884 typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify;
9885 
9886 
9887 /*
9888 COM objects. The way these work is that you have a vtable (a list of function pointers, kind of
9889 like how C++ works internally), and then you have a structure with a single member, which is a
9890 pointer to the vtable. The vtable is where the methods of the object are defined. Methods need
9891 to be in a specific order, and parent classes need to have their methods declared first.
9892 */
9893 
9894 /* IDirectSound */
9895 typedef struct
9896 {
9897  /* IUnknown */
9898  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject);
9899  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis);
9900  ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis);
9901 
9902  /* IDirectSound */
9903  HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter);
9904  HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps);
9905  HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate);
9906  HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel);
9907  HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis);
9908  HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig);
9909  HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig);
9910  HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice);
9911 } ma_IDirectSoundVtbl;
9912 struct ma_IDirectSound
9913 {
9914  ma_IDirectSoundVtbl* lpVtbl;
9915 };
9916 HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
9917 ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); }
9918 ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); }
9919 HRESULT ma_IDirectSound_CreateSoundBuffer(ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); }
9920 HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); }
9921 HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); }
9922 HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); }
9923 HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); }
9924 HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); }
9925 HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); }
9926 HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
9927 
9928 
9929 /* IDirectSoundBuffer */
9930 typedef struct
9931 {
9932  /* IUnknown */
9933  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject);
9934  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis);
9935  ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis);
9936 
9937  /* IDirectSoundBuffer */
9938  HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps);
9939  HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor);
9940  HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
9941  HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume);
9942  HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan);
9943  HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency);
9944  HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus);
9945  HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc);
9946  HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
9947  HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags);
9948  HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition);
9949  HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat);
9950  HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume);
9951  HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan);
9952  HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency);
9953  HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis);
9954  HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
9955  HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis);
9956 } ma_IDirectSoundBufferVtbl;
9957 struct ma_IDirectSoundBuffer
9958 {
9959  ma_IDirectSoundBufferVtbl* lpVtbl;
9960 };
9961 HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
9962 ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
9963 ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
9964 HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); }
9965 HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); }
9966 HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
9967 HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); }
9968 HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); }
9969 HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); }
9970 HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
9971 HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); }
9972 HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); }
9973 HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); }
9974 HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); }
9975 HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); }
9976 HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); }
9977 HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); }
9978 HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); }
9979 HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
9980 HRESULT ma_IDirectSoundBuffer_Unlock(ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); }
9981 HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); }
9982 
9983 
9984 /* IDirectSoundCapture */
9985 typedef struct
9986 {
9987  /* IUnknown */
9988  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject);
9989  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis);
9990  ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis);
9991 
9992  /* IDirectSoundCapture */
9993  HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter);
9994  HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps);
9995  HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice);
9996 } ma_IDirectSoundCaptureVtbl;
9997 struct ma_IDirectSoundCapture
9998 {
9999  ma_IDirectSoundCaptureVtbl* lpVtbl;
10000 };
10001 HRESULT ma_IDirectSoundCapture_QueryInterface(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
10002 ULONG ma_IDirectSoundCapture_AddRef(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); }
10003 ULONG ma_IDirectSoundCapture_Release(ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); }
10004 HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); }
10005 HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); }
10006 HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
10007 
10008 
10009 /* IDirectSoundCaptureBuffer */
10010 typedef struct
10011 {
10012  /* IUnknown */
10013  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject);
10014  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis);
10015  ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis);
10016 
10017  /* IDirectSoundCaptureBuffer */
10018  HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps);
10019  HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition);
10020  HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
10021  HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus);
10022  HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc);
10023  HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
10024  HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags);
10025  HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis);
10026  HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
10027 } ma_IDirectSoundCaptureBufferVtbl;
10028 struct ma_IDirectSoundCaptureBuffer
10029 {
10030  ma_IDirectSoundCaptureBufferVtbl* lpVtbl;
10031 };
10032 HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
10033 ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
10034 ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
10035 HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); }
10036 HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); }
10037 HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
10038 HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
10039 HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); }
10040 HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); }
10041 HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); }
10042 HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
10043 HRESULT ma_IDirectSoundCaptureBuffer_Unlock(ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); }
10044 
10045 
10046 /* IDirectSoundNotify */
10047 typedef struct
10048 {
10049  /* IUnknown */
10050  HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject);
10051  ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis);
10052  ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis);
10053 
10054  /* IDirectSoundNotify */
10055  HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies);
10056 } ma_IDirectSoundNotifyVtbl;
10057 struct ma_IDirectSoundNotify
10058 {
10059  ma_IDirectSoundNotifyVtbl* lpVtbl;
10060 };
10061 HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
10062 ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); }
10063 ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); }
10064 HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); }
10065 
10066 
10067 typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (LPGUID pDeviceGUID, LPCSTR pDeviceDescription, LPCSTR pModule, LPVOID pContext);
10068 typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, LPUNKNOWN pUnkOuter);
10069 typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext);
10070 typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, LPUNKNOWN pUnkOuter);
10071 typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext);
10072 
10073 
10074 /*
10075 Retrieves the channel count and channel map for the given speaker configuration. If the speaker configuration is unknown,
10076 the channel count and channel map will be left unmodified.
10077 */
10078 void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut)
10079 {
10080  WORD channels;
10081  DWORD channelMap;
10082 
10083  channels = 0;
10084  if (pChannelsOut != NULL) {
10085  channels = *pChannelsOut;
10086  }
10087 
10088  channelMap = 0;
10089  if (pChannelMapOut != NULL) {
10090  channelMap = *pChannelMapOut;
10091  }
10092 
10093  /*
10094  The speaker configuration is a combination of speaker config and speaker geometry. The lower 8 bits is what we care about. The upper
10095  16 bits is for the geometry.
10096  */
10097  switch ((BYTE)(speakerConfig)) {
10098  case 1 /*DSSPEAKER_HEADPHONE*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
10099  case 2 /*DSSPEAKER_MONO*/: channels = 1; channelMap = SPEAKER_FRONT_CENTER; break;
10100  case 3 /*DSSPEAKER_QUAD*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
10101  case 4 /*DSSPEAKER_STEREO*/: channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
10102  case 5 /*DSSPEAKER_SURROUND*/: channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break;
10103  case 6 /*DSSPEAKER_5POINT1_BACK*/ /*DSSPEAKER_5POINT1*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
10104  case 7 /*DSSPEAKER_7POINT1_WIDE*/ /*DSSPEAKER_7POINT1*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER; break;
10105  case 8 /*DSSPEAKER_7POINT1_SURROUND*/: channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break;
10106  case 9 /*DSSPEAKER_5POINT1_SURROUND*/: channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break;
10107  default: break;
10108  }
10109 
10110  if (pChannelsOut != NULL) {
10111  *pChannelsOut = channels;
10112  }
10113 
10114  if (pChannelMapOut != NULL) {
10115  *pChannelMapOut = channelMap;
10116  }
10117 }
10118 
10119 
10120 ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound)
10121 {
10122  ma_IDirectSound* pDirectSound;
10123  HWND hWnd;
10124 
10125  ma_assert(pContext != NULL);
10126  ma_assert(ppDirectSound != NULL);
10127 
10128  *ppDirectSound = NULL;
10129  pDirectSound = NULL;
10130 
10131  if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) {
10132  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
10133  }
10134 
10135  /* The cooperative level must be set before doing anything else. */
10136  hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
10137  if (hWnd == NULL) {
10138  hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
10139  }
10140  if (FAILED(ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY))) {
10141  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.", MA_SHARE_MODE_NOT_SUPPORTED);
10142  }
10143 
10144  *ppDirectSound = pDirectSound;
10145  return MA_SUCCESS;
10146 }
10147 
10148 ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture)
10149 {
10150  ma_IDirectSoundCapture* pDirectSoundCapture;
10151 
10152  ma_assert(pContext != NULL);
10153  ma_assert(ppDirectSoundCapture != NULL);
10154 
10155  /* DirectSound does not support exclusive mode for capture. */
10156  if (shareMode == ma_share_mode_exclusive) {
10158  }
10159 
10160  *ppDirectSoundCapture = NULL;
10161  pDirectSoundCapture = NULL;
10162 
10163  if (FAILED(((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL))) {
10164  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
10165  }
10166 
10167  *ppDirectSoundCapture = pDirectSoundCapture;
10168  return MA_SUCCESS;
10169 }
10170 
10171 ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate)
10172 {
10173  MA_DSCCAPS caps;
10174  WORD bitsPerSample;
10175  DWORD sampleRate;
10176 
10177  ma_assert(pContext != NULL);
10178  ma_assert(pDirectSoundCapture != NULL);
10179 
10180  if (pChannels) {
10181  *pChannels = 0;
10182  }
10183  if (pBitsPerSample) {
10184  *pBitsPerSample = 0;
10185  }
10186  if (pSampleRate) {
10187  *pSampleRate = 0;
10188  }
10189 
10190  ma_zero_object(&caps);
10191  caps.dwSize = sizeof(caps);
10192  if (FAILED(ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps))) {
10193  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
10194  }
10195 
10196  if (pChannels) {
10197  *pChannels = (WORD)caps.dwChannels;
10198  }
10199 
10200  /* The device can support multiple formats. We just go through the different formats in order of priority and pick the first one. This the same type of system as the WinMM backend. */
10201  bitsPerSample = 16;
10202  sampleRate = 48000;
10203 
10204  if (caps.dwChannels == 1) {
10205  if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) {
10206  sampleRate = 48000;
10207  } else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) {
10208  sampleRate = 44100;
10209  } else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) {
10210  sampleRate = 22050;
10211  } else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) {
10212  sampleRate = 11025;
10213  } else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) {
10214  sampleRate = 96000;
10215  } else {
10216  bitsPerSample = 8;
10217  if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) {
10218  sampleRate = 48000;
10219  } else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) {
10220  sampleRate = 44100;
10221  } else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) {
10222  sampleRate = 22050;
10223  } else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) {
10224  sampleRate = 11025;
10225  } else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) {
10226  sampleRate = 96000;
10227  } else {
10228  bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
10229  }
10230  }
10231  } else if (caps.dwChannels == 2) {
10232  if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) {
10233  sampleRate = 48000;
10234  } else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) {
10235  sampleRate = 44100;
10236  } else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) {
10237  sampleRate = 22050;
10238  } else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) {
10239  sampleRate = 11025;
10240  } else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) {
10241  sampleRate = 96000;
10242  } else {
10243  bitsPerSample = 8;
10244  if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) {
10245  sampleRate = 48000;
10246  } else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) {
10247  sampleRate = 44100;
10248  } else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) {
10249  sampleRate = 22050;
10250  } else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) {
10251  sampleRate = 11025;
10252  } else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) {
10253  sampleRate = 96000;
10254  } else {
10255  bitsPerSample = 16; /* Didn't find it. Just fall back to 16-bit. */
10256  }
10257  }
10258  }
10259 
10260  if (pBitsPerSample) {
10261  *pBitsPerSample = bitsPerSample;
10262  }
10263  if (pSampleRate) {
10264  *pSampleRate = sampleRate;
10265  }
10266 
10267  return MA_SUCCESS;
10268 }
10269 
10270 ma_bool32 ma_context_is_device_id_equal__dsound(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
10271 {
10272  ma_assert(pContext != NULL);
10273  ma_assert(pID0 != NULL);
10274  ma_assert(pID1 != NULL);
10275  (void)pContext;
10276 
10277  return memcmp(pID0->dsound, pID1->dsound, sizeof(pID0->dsound)) == 0;
10278 }
10279 
10280 
10281 typedef struct
10282 {
10283  ma_context* pContext;
10284  ma_device_type deviceType;
10286  void* pUserData;
10287  ma_bool32 terminated;
10288 } ma_context_enumerate_devices_callback_data__dsound;
10289 
10290 BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
10291 {
10292  ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext;
10293  ma_device_info deviceInfo;
10294 
10295  ma_zero_object(&deviceInfo);
10296 
10297  /* ID. */
10298  if (lpGuid != NULL) {
10299  ma_copy_memory(deviceInfo.id.dsound, lpGuid, 16);
10300  } else {
10301  ma_zero_memory(deviceInfo.id.dsound, 16);
10302  }
10303 
10304  /* Name / Description */
10305  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1);
10306 
10307 
10308  /* Call the callback function, but make sure we stop enumerating if the callee requested so. */
10309  ma_assert(pData != NULL);
10310  pData->terminated = !pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData);
10311  if (pData->terminated) {
10312  return FALSE; /* Stop enumeration. */
10313  } else {
10314  return TRUE; /* Continue enumeration. */
10315  }
10316 
10317  (void)lpcstrModule;
10318 }
10319 
10320 ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
10321 {
10322  ma_context_enumerate_devices_callback_data__dsound data;
10323 
10324  ma_assert(pContext != NULL);
10325  ma_assert(callback != NULL);
10326 
10327  data.pContext = pContext;
10328  data.callback = callback;
10329  data.pUserData = pUserData;
10330  data.terminated = MA_FALSE;
10331 
10332  /* Playback. */
10333  if (!data.terminated) {
10334  data.deviceType = ma_device_type_playback;
10335  ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
10336  }
10337 
10338  /* Capture. */
10339  if (!data.terminated) {
10340  data.deviceType = ma_device_type_capture;
10341  ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
10342  }
10343 
10344  return MA_SUCCESS;
10345 }
10346 
10347 
10348 typedef struct
10349 {
10350  const ma_device_id* pDeviceID;
10351  ma_device_info* pDeviceInfo;
10352  ma_bool32 found;
10353 } ma_context_get_device_info_callback_data__dsound;
10354 
10355 BOOL CALLBACK ma_context_get_device_info_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
10356 {
10357  ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext;
10358  ma_assert(pData != NULL);
10359 
10360  if ((pData->pDeviceID == NULL || ma_is_guid_equal(pData->pDeviceID->dsound, &MA_GUID_NULL)) && (lpGuid == NULL || ma_is_guid_equal(lpGuid, &MA_GUID_NULL))) {
10361  /* Default device. */
10362  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
10363  pData->found = MA_TRUE;
10364  return FALSE; /* Stop enumeration. */
10365  } else {
10366  /* Not the default device. */
10367  if (lpGuid != NULL) {
10368  if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) {
10369  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
10370  pData->found = MA_TRUE;
10371  return FALSE; /* Stop enumeration. */
10372  }
10373  }
10374  }
10375 
10376  (void)lpcstrModule;
10377  return TRUE;
10378 }
10379 
10380 ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
10381 {
10382  /* Exclusive mode and capture not supported with DirectSound. */
10383  if (deviceType == ma_device_type_capture && shareMode == ma_share_mode_exclusive) {
10385  }
10386 
10387  if (pDeviceID != NULL) {
10388  ma_context_get_device_info_callback_data__dsound data;
10389 
10390  /* ID. */
10391  ma_copy_memory(pDeviceInfo->id.dsound, pDeviceID->dsound, 16);
10392 
10393  /* Name / Description. This is retrieved by enumerating over each device until we find that one that matches the input ID. */
10394  data.pDeviceID = pDeviceID;
10395  data.pDeviceInfo = pDeviceInfo;
10396  data.found = MA_FALSE;
10397  if (deviceType == ma_device_type_playback) {
10398  ((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
10399  } else {
10400  ((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
10401  }
10402 
10403  if (!data.found) {
10404  return MA_NO_DEVICE;
10405  }
10406  } else {
10407  /* I don't think there's a way to get the name of the default device with DirectSound. In this case we just need to use defaults. */
10408 
10409  /* ID */
10410  ma_zero_memory(pDeviceInfo->id.dsound, 16);
10411 
10412  /* Name / Description */
10413  if (deviceType == ma_device_type_playback) {
10414  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
10415  } else {
10416  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
10417  }
10418  }
10419 
10420  /* Retrieving detailed information is slightly different depending on the device type. */
10421  if (deviceType == ma_device_type_playback) {
10422  /* Playback. */
10423  ma_IDirectSound* pDirectSound;
10424  ma_result result;
10425  MA_DSCAPS caps;
10426  ma_uint32 iFormat;
10427 
10428  result = ma_context_create_IDirectSound__dsound(pContext, shareMode, pDeviceID, &pDirectSound);
10429  if (result != MA_SUCCESS) {
10430  return result;
10431  }
10432 
10433  ma_zero_object(&caps);
10434  caps.dwSize = sizeof(caps);
10435  if (FAILED(ma_IDirectSound_GetCaps(pDirectSound, &caps))) {
10436  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
10437  }
10438 
10439  if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
10440  /* It supports at least stereo, but could support more. */
10441  WORD channels = 2;
10442 
10443  /* Look at the speaker configuration to get a better idea on the channel count. */
10444  DWORD speakerConfig;
10445  if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig))) {
10446  ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL);
10447  }
10448 
10449  pDeviceInfo->minChannels = channels;
10450  pDeviceInfo->maxChannels = channels;
10451  } else {
10452  /* It does not support stereo, which means we are stuck with mono. */
10453  pDeviceInfo->minChannels = 1;
10454  pDeviceInfo->maxChannels = 1;
10455  }
10456 
10457  /* Sample rate. */
10458  if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
10459  pDeviceInfo->minSampleRate = caps.dwMinSecondarySampleRate;
10460  pDeviceInfo->maxSampleRate = caps.dwMaxSecondarySampleRate;
10461 
10462  /*
10463  On my machine the min and max sample rates can return 100 and 200000 respectively. I'd rather these be within
10464  the range of our standard sample rates so I'm clamping.
10465  */
10466  if (caps.dwMinSecondarySampleRate < MA_MIN_SAMPLE_RATE && caps.dwMaxSecondarySampleRate >= MA_MIN_SAMPLE_RATE) {
10467  pDeviceInfo->minSampleRate = MA_MIN_SAMPLE_RATE;
10468  }
10469  if (caps.dwMaxSecondarySampleRate > MA_MAX_SAMPLE_RATE && caps.dwMinSecondarySampleRate <= MA_MAX_SAMPLE_RATE) {
10470  pDeviceInfo->maxSampleRate = MA_MAX_SAMPLE_RATE;
10471  }
10472  } else {
10473  /* Only supports a single sample rate. Set both min an max to the same thing. Do not clamp within the standard rates. */
10474  pDeviceInfo->minSampleRate = caps.dwMaxSecondarySampleRate;
10475  pDeviceInfo->maxSampleRate = caps.dwMaxSecondarySampleRate;
10476  }
10477 
10478  /* DirectSound can support all formats. */
10479  pDeviceInfo->formatCount = ma_format_count - 1; /* Minus one because we don't want to include ma_format_unknown. */
10480  for (iFormat = 0; iFormat < pDeviceInfo->formatCount; ++iFormat) {
10481  pDeviceInfo->formats[iFormat] = (ma_format)(iFormat + 1); /* +1 to skip over ma_format_unknown. */
10482  }
10483 
10484  ma_IDirectSound_Release(pDirectSound);
10485  } else {
10486  /*
10487  Capture. This is a little different to playback due to the say the supported formats are reported. Technically capture
10488  devices can support a number of different formats, but for simplicity and consistency with ma_device_init() I'm just
10489  reporting the best format.
10490  */
10491  ma_IDirectSoundCapture* pDirectSoundCapture;
10492  ma_result result;
10493  WORD channels;
10494  WORD bitsPerSample;
10495  DWORD sampleRate;
10496 
10497  result = ma_context_create_IDirectSoundCapture__dsound(pContext, shareMode, pDeviceID, &pDirectSoundCapture);
10498  if (result != MA_SUCCESS) {
10499  return result;
10500  }
10501 
10502  result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate);
10503  if (result != MA_SUCCESS) {
10504  ma_IDirectSoundCapture_Release(pDirectSoundCapture);
10505  return result;
10506  }
10507 
10508  pDeviceInfo->minChannels = channels;
10509  pDeviceInfo->maxChannels = channels;
10510  pDeviceInfo->minSampleRate = sampleRate;
10511  pDeviceInfo->maxSampleRate = sampleRate;
10512  pDeviceInfo->formatCount = 1;
10513  if (bitsPerSample == 8) {
10514  pDeviceInfo->formats[0] = ma_format_u8;
10515  } else if (bitsPerSample == 16) {
10516  pDeviceInfo->formats[0] = ma_format_s16;
10517  } else if (bitsPerSample == 24) {
10518  pDeviceInfo->formats[0] = ma_format_s24;
10519  } else if (bitsPerSample == 32) {
10520  pDeviceInfo->formats[0] = ma_format_s32;
10521  } else {
10522  ma_IDirectSoundCapture_Release(pDirectSoundCapture);
10523  return MA_FORMAT_NOT_SUPPORTED;
10524  }
10525 
10526  ma_IDirectSoundCapture_Release(pDirectSoundCapture);
10527  }
10528 
10529  return MA_SUCCESS;
10530 }
10531 
10532 
10533 typedef struct
10534 {
10535  ma_uint32 deviceCount;
10536  ma_uint32 infoCount;
10537  ma_device_info* pInfo;
10538 } ma_device_enum_data__dsound;
10539 
10540 BOOL CALLBACK ma_enum_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext)
10541 {
10542  ma_device_enum_data__dsound* pData = (ma_device_enum_data__dsound*)lpContext;
10543  ma_assert(pData != NULL);
10544 
10545  if (pData->pInfo != NULL) {
10546  if (pData->infoCount > 0) {
10547  ma_zero_object(pData->pInfo);
10548  ma_strncpy_s(pData->pInfo->name, sizeof(pData->pInfo->name), lpcstrDescription, (size_t)-1);
10549 
10550  if (lpGuid != NULL) {
10551  ma_copy_memory(pData->pInfo->id.dsound, lpGuid, 16);
10552  } else {
10553  ma_zero_memory(pData->pInfo->id.dsound, 16);
10554  }
10555 
10556  pData->pInfo += 1;
10557  pData->infoCount -= 1;
10558  pData->deviceCount += 1;
10559  }
10560  } else {
10561  pData->deviceCount += 1;
10562  }
10563 
10564  (void)lpcstrModule;
10565  return TRUE;
10566 }
10567 
10568 void ma_device_uninit__dsound(ma_device* pDevice)
10569 {
10570  ma_assert(pDevice != NULL);
10571 
10572  if (pDevice->dsound.pCaptureBuffer != NULL) {
10573  ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
10574  }
10575  if (pDevice->dsound.pCapture != NULL) {
10576  ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture);
10577  }
10578 
10579  if (pDevice->dsound.pPlaybackBuffer != NULL) {
10580  ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
10581  }
10582  if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) {
10583  ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer);
10584  }
10585  if (pDevice->dsound.pPlayback != NULL) {
10586  ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback);
10587  }
10588 }
10589 
10590 ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, WAVEFORMATEXTENSIBLE* pWF)
10591 {
10592  GUID subformat;
10593 
10594  switch (format)
10595  {
10596  case ma_format_u8:
10597  case ma_format_s16:
10598  case ma_format_s24:
10599  /*case ma_format_s24_32:*/
10600  case ma_format_s32:
10601  {
10602  subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
10603  } break;
10604 
10605  case ma_format_f32:
10606  {
10607  subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
10608  } break;
10609 
10610  default:
10611  return MA_FORMAT_NOT_SUPPORTED;
10612  }
10613 
10614  ma_zero_object(pWF);
10615  pWF->Format.cbSize = sizeof(*pWF);
10616  pWF->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
10617  pWF->Format.nChannels = (WORD)channels;
10618  pWF->Format.nSamplesPerSec = (DWORD)sampleRate;
10619  pWF->Format.wBitsPerSample = (WORD)ma_get_bytes_per_sample(format)*8;
10620  pWF->Format.nBlockAlign = (pWF->Format.nChannels * pWF->Format.wBitsPerSample) / 8;
10621  pWF->Format.nAvgBytesPerSec = pWF->Format.nBlockAlign * pWF->Format.nSamplesPerSec;
10622  pWF->Samples.wValidBitsPerSample = pWF->Format.wBitsPerSample;
10623  pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels);
10624  pWF->SubFormat = subformat;
10625 
10626  return MA_SUCCESS;
10627 }
10628 
10629 ma_result ma_device_init__dsound(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
10630 {
10631  ma_result result;
10632  ma_uint32 bufferSizeInMilliseconds;
10633 
10634  ma_assert(pDevice != NULL);
10635  ma_zero_object(&pDevice->dsound);
10636 
10637  if (pConfig->deviceType == ma_device_type_loopback) {
10639  }
10640 
10641  bufferSizeInMilliseconds = pConfig->bufferSizeInMilliseconds;
10642  if (bufferSizeInMilliseconds == 0) {
10643  bufferSizeInMilliseconds = ma_calculate_buffer_size_in_milliseconds_from_frames(pConfig->bufferSizeInFrames, pConfig->sampleRate);
10644  }
10645 
10646  /* DirectSound should use a latency of about 20ms per period for low latency mode. */
10647  if (pDevice->usingDefaultBufferSize) {
10649  bufferSizeInMilliseconds = 20 * pConfig->periods;
10650  } else {
10651  bufferSizeInMilliseconds = 200 * pConfig->periods;
10652  }
10653  }
10654 
10655  /* DirectSound breaks down with tiny buffer sizes (bad glitching and silent output). I am therefore restricting the size of the buffer to a minimum of 20 milliseconds. */
10656  if ((bufferSizeInMilliseconds/pConfig->periods) < 20) {
10657  bufferSizeInMilliseconds = pConfig->periods * 20;
10658  }
10659 
10660  /*
10661  Unfortunately DirectSound uses different APIs and data structures for playback and catpure devices. We need to initialize
10662  the capture device first because we'll want to match it's buffer size and period count on the playback side if we're using
10663  full-duplex mode.
10664  */
10665  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
10666  WAVEFORMATEXTENSIBLE wf;
10667  MA_DSCBUFFERDESC descDS;
10668  ma_uint32 bufferSizeInFrames;
10669  char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
10670  WAVEFORMATEXTENSIBLE* pActualFormat;
10671 
10672  result = ma_config_to_WAVEFORMATEXTENSIBLE(pConfig->capture.format, pConfig->capture.channels, pConfig->sampleRate, pConfig->capture.channelMap, &wf);
10673  if (result != MA_SUCCESS) {
10674  return result;
10675  }
10676 
10677  result = ma_context_create_IDirectSoundCapture__dsound(pContext, pConfig->capture.shareMode, pConfig->capture.pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture);
10678  if (result != MA_SUCCESS) {
10679  ma_device_uninit__dsound(pDevice);
10680  return result;
10681  }
10682 
10683  result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.Format.nChannels, &wf.Format.wBitsPerSample, &wf.Format.nSamplesPerSec);
10684  if (result != MA_SUCCESS) {
10685  ma_device_uninit__dsound(pDevice);
10686  return result;
10687  }
10688 
10689  wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
10690  wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
10691  wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample;
10692  wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
10693 
10694  /* The size of the buffer must be a clean multiple of the period count. */
10695  bufferSizeInFrames = (ma_calculate_buffer_size_in_frames_from_milliseconds(bufferSizeInMilliseconds, wf.Format.nSamplesPerSec) / pConfig->periods) * pConfig->periods;
10696 
10697  ma_zero_object(&descDS);
10698  descDS.dwSize = sizeof(descDS);
10699  descDS.dwFlags = 0;
10700  descDS.dwBufferBytes = bufferSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, wf.Format.nChannels);
10701  descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
10702  if (FAILED(ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL))) {
10703  ma_device_uninit__dsound(pDevice);
10704  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
10705  }
10706 
10707  /* Get the _actual_ properties of the buffer. */
10708  pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
10709  if (FAILED(ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL))) {
10710  ma_device_uninit__dsound(pDevice);
10711  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer.", MA_FORMAT_NOT_SUPPORTED);
10712  }
10713 
10714  pDevice->capture.internalFormat = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat);
10715  pDevice->capture.internalChannels = pActualFormat->Format.nChannels;
10716  pDevice->capture.internalSampleRate = pActualFormat->Format.nSamplesPerSec;
10717 
10718  /* Get the internal channel map based on the channel mask. */
10719  if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
10720  ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
10721  } else {
10722  ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
10723  }
10724 
10725  /*
10726  After getting the actual format the size of the buffer in frames may have actually changed. However, we want this to be as close to what the
10727  user has asked for as possible, so let's go ahead and release the old capture buffer and create a new one in this case.
10728  */
10729  if (bufferSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels))) {
10730  descDS.dwBufferBytes = bufferSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, wf.Format.nChannels);
10731  ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
10732 
10733  if (FAILED(ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL))) {
10734  ma_device_uninit__dsound(pDevice);
10735  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
10736  }
10737  }
10738 
10739  /* DirectSound should give us a buffer exactly the size we asked for. */
10740  pDevice->capture.internalBufferSizeInFrames = bufferSizeInFrames;
10741  pDevice->capture.internalPeriods = pConfig->periods;
10742  }
10743 
10744  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
10745  WAVEFORMATEXTENSIBLE wf;
10746  MA_DSBUFFERDESC descDSPrimary;
10747  MA_DSCAPS caps;
10748  char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */
10749  WAVEFORMATEXTENSIBLE* pActualFormat;
10750  ma_uint32 bufferSizeInFrames;
10751  MA_DSBUFFERDESC descDS;
10752 
10753  result = ma_config_to_WAVEFORMATEXTENSIBLE(pConfig->playback.format, pConfig->playback.channels, pConfig->sampleRate, pConfig->playback.channelMap, &wf);
10754  if (result != MA_SUCCESS) {
10755  return result;
10756  }
10757 
10758  result = ma_context_create_IDirectSound__dsound(pContext, pConfig->playback.shareMode, pConfig->playback.pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback);
10759  if (result != MA_SUCCESS) {
10760  ma_device_uninit__dsound(pDevice);
10761  return result;
10762  }
10763 
10764  ma_zero_object(&descDSPrimary);
10765  descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC);
10766  descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME;
10767  if (FAILED(ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL))) {
10768  ma_device_uninit__dsound(pDevice);
10769  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
10770  }
10771 
10772 
10773  /* We may want to make some adjustments to the format if we are using defaults. */
10774  ma_zero_object(&caps);
10775  caps.dwSize = sizeof(caps);
10776  if (FAILED(ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps))) {
10777  ma_device_uninit__dsound(pDevice);
10778  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
10779  }
10780 
10781  if (pDevice->playback.usingDefaultChannels) {
10782  if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
10783  DWORD speakerConfig;
10784 
10785  /* It supports at least stereo, but could support more. */
10786  wf.Format.nChannels = 2;
10787 
10788  /* Look at the speaker configuration to get a better idea on the channel count. */
10789  if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) {
10790  ma_get_channels_from_speaker_config__dsound(speakerConfig, &wf.Format.nChannels, &wf.dwChannelMask);
10791  }
10792  } else {
10793  /* It does not support stereo, which means we are stuck with mono. */
10794  wf.Format.nChannels = 1;
10795  }
10796  }
10797 
10798  if (pDevice->usingDefaultSampleRate) {
10799  /* We base the sample rate on the values returned by GetCaps(). */
10800  if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
10801  wf.Format.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate);
10802  } else {
10803  wf.Format.nSamplesPerSec = caps.dwMaxSecondarySampleRate;
10804  }
10805  }
10806 
10807  wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8;
10808  wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec;
10809 
10810  /*
10811  From MSDN:
10812 
10813  The method succeeds even if the hardware does not support the requested format; DirectSound sets the buffer to the closest
10814  supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer
10815  and compare the result with the format that was requested with the SetFormat method.
10816  */
10817  if (FAILED(ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)&wf))) {
10818  ma_device_uninit__dsound(pDevice);
10819  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer.", MA_FORMAT_NOT_SUPPORTED);
10820  }
10821 
10822  /* Get the _actual_ properties of the buffer. */
10823  pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata;
10824  if (FAILED(ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL))) {
10825  ma_device_uninit__dsound(pDevice);
10826  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer.", MA_FORMAT_NOT_SUPPORTED);
10827  }
10828 
10829  pDevice->playback.internalFormat = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat);
10830  pDevice->playback.internalChannels = pActualFormat->Format.nChannels;
10831  pDevice->playback.internalSampleRate = pActualFormat->Format.nSamplesPerSec;
10832 
10833  /* Get the internal channel map based on the channel mask. */
10834  if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
10835  ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
10836  } else {
10837  ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
10838  }
10839 
10840  /* The size of the buffer must be a clean multiple of the period count. */
10841  bufferSizeInFrames = (ma_calculate_buffer_size_in_frames_from_milliseconds(bufferSizeInMilliseconds, pDevice->playback.internalSampleRate) / pConfig->periods) * pConfig->periods;
10842 
10843  /*
10844  Meaning of dwFlags (from MSDN):
10845 
10846  DSBCAPS_CTRLPOSITIONNOTIFY
10847  The buffer has position notification capability.
10848 
10849  DSBCAPS_GLOBALFOCUS
10850  With this flag set, an application using DirectSound can continue to play its buffers if the user switches focus to
10851  another application, even if the new application uses DirectSound.
10852 
10853  DSBCAPS_GETCURRENTPOSITION2
10854  In the first version of DirectSound, the play cursor was significantly ahead of the actual playing sound on emulated
10855  sound cards; it was directly behind the write cursor. Now, if the DSBCAPS_GETCURRENTPOSITION2 flag is specified, the
10856  application can get a more accurate play cursor.
10857  */
10858  ma_zero_object(&descDS);
10859  descDS.dwSize = sizeof(descDS);
10860  descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2;
10861  descDS.dwBufferBytes = bufferSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
10862  descDS.lpwfxFormat = (WAVEFORMATEX*)&wf;
10863  if (FAILED(ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL))) {
10864  ma_device_uninit__dsound(pDevice);
10865  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
10866  }
10867 
10868  /* DirectSound should give us a buffer exactly the size we asked for. */
10869  pDevice->playback.internalBufferSizeInFrames = bufferSizeInFrames;
10870  pDevice->playback.internalPeriods = pConfig->periods;
10871  }
10872 
10873  (void)pContext;
10874  return MA_SUCCESS;
10875 }
10876 
10877 
10878 ma_result ma_device_main_loop__dsound(ma_device* pDevice)
10879 {
10881  ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
10882  ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
10883  HRESULT hr;
10884  DWORD lockOffsetInBytesCapture;
10885  DWORD lockSizeInBytesCapture;
10886  DWORD mappedSizeInBytesCapture;
10887  void* pMappedBufferCapture;
10888  DWORD lockOffsetInBytesPlayback;
10889  DWORD lockSizeInBytesPlayback;
10890  DWORD mappedSizeInBytesPlayback;
10891  void* pMappedBufferPlayback;
10892  DWORD prevReadCursorInBytesCapture = 0;
10893  DWORD prevPlayCursorInBytesPlayback = 0;
10894  ma_bool32 physicalPlayCursorLoopFlagPlayback = 0;
10895  DWORD virtualWriteCursorInBytesPlayback = 0;
10896  ma_bool32 virtualWriteCursorLoopFlagPlayback = 0;
10897  ma_bool32 isPlaybackDeviceStarted = MA_FALSE;
10898  ma_uint32 framesWrittenToPlaybackDevice = 0; /* For knowing whether or not the playback device needs to be started. */
10899  ma_uint32 waitTimeInMilliseconds = 1;
10900 
10901  ma_assert(pDevice != NULL);
10902 
10903  /* The first thing to do is start the capture device. The playback device is only started after the first period is written. */
10904  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
10905  if (FAILED(ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING))) {
10906  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
10907  }
10908  }
10909 
10910  while (ma_device__get_state(pDevice) == MA_STATE_STARTED) {
10911  switch (pDevice->type)
10912  {
10913  case ma_device_type_duplex:
10914  {
10915  DWORD physicalCaptureCursorInBytes;
10916  DWORD physicalReadCursorInBytes;
10917  if (FAILED(ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes))) {
10918  return MA_ERROR;
10919  }
10920 
10921  /* If nothing is available we just sleep for a bit and return from this iteration. */
10922  if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) {
10923  ma_sleep(waitTimeInMilliseconds);
10924  continue; /* Nothing is available in the capture buffer. */
10925  }
10926 
10927  /*
10928  The current position has moved. We need to map all of the captured samples and write them to the playback device, making sure
10929  we don't return until every frame has been copied over.
10930  */
10931  if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
10932  /* The capture position has not looped. This is the simple case. */
10933  lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
10934  lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
10935  } else {
10936  /*
10937  The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
10938  do it again from the start.
10939  */
10940  if (prevReadCursorInBytesCapture < pDevice->capture.internalBufferSizeInFrames*bpfCapture) {
10941  /* Lock up to the end of the buffer. */
10942  lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
10943  lockSizeInBytesCapture = (pDevice->capture.internalBufferSizeInFrames*bpfCapture) - prevReadCursorInBytesCapture;
10944  } else {
10945  /* Lock starting from the start of the buffer. */
10946  lockOffsetInBytesCapture = 0;
10947  lockSizeInBytesCapture = physicalReadCursorInBytes;
10948  }
10949  }
10950 
10951  if (lockSizeInBytesCapture == 0) {
10952  ma_sleep(waitTimeInMilliseconds);
10953  continue; /* Nothing is available in the capture buffer. */
10954  }
10955 
10956  hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
10957  if (FAILED(hr)) {
10958  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
10959  }
10960 
10961 
10962  /* At this point we have some input data that we need to output. We do not return until every mapped frame of the input data is written to the playback device. */
10963  pDevice->capture._dspFrameCount = mappedSizeInBytesCapture / bpfCapture;
10964  pDevice->capture._dspFrames = (const ma_uint8*)pMappedBufferCapture;
10965  for (;;) { /* Keep writing to the playback device. */
10966  ma_uint8 inputFramesInExternalFormat[4096];
10967  ma_uint32 inputFramesInExternalFormatCap = sizeof(inputFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
10968  ma_uint32 inputFramesInExternalFormatCount;
10969  ma_uint8 outputFramesInExternalFormat[4096];
10970  ma_uint32 outputFramesInExternalFormatCap = sizeof(outputFramesInExternalFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
10971 
10972  inputFramesInExternalFormatCount = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, inputFramesInExternalFormat, ma_min(inputFramesInExternalFormatCap, outputFramesInExternalFormatCap));
10973  if (inputFramesInExternalFormatCount == 0) {
10974  break; /* No more input data. */
10975  }
10976 
10977  ma_device__on_data(pDevice, outputFramesInExternalFormat, inputFramesInExternalFormat, inputFramesInExternalFormatCount);
10978 
10979  /* At this point we have input and output data in external format. All we need to do now is convert it to the output format. This may take a few passes. */
10980  pDevice->playback._dspFrameCount = inputFramesInExternalFormatCount;
10981  pDevice->playback._dspFrames = (const ma_uint8*)outputFramesInExternalFormat;
10982  for (;;) {
10983  ma_uint32 framesWrittenThisIteration;
10984  DWORD physicalPlayCursorInBytes;
10985  DWORD physicalWriteCursorInBytes;
10986  DWORD availableBytesPlayback;
10987  DWORD silentPaddingInBytes = 0; /* <-- Must be initialized to 0. */
10988 
10989  /* We need the physical play and write cursors. */
10990  if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
10991  break;
10992  }
10993 
10994  if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
10995  physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
10996  }
10997  prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
10998 
10999  /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
11000  if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
11001  /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
11002  if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
11003  availableBytesPlayback = (pDevice->playback.internalBufferSizeInFrames*bpfPlayback) - virtualWriteCursorInBytesPlayback;
11004  availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
11005  } else {
11006  /* This is an error. */
11007  #ifdef MA_DEBUG_OUTPUT
11008  printf("[DirectSound] (Duplex/Playback) WARNING: Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%d, virtualWriteCursorInBytes=%d.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
11009  #endif
11010  availableBytesPlayback = 0;
11011  }
11012  } else {
11013  /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
11014  if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
11015  availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
11016  } else {
11017  /* This is an error. */
11018  #ifdef MA_DEBUG_OUTPUT
11019  printf("[DirectSound] (Duplex/Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%d, virtualWriteCursorInBytes=%d.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
11020  #endif
11021  availableBytesPlayback = 0;
11022  }
11023  }
11024 
11025  #ifdef MA_DEBUG_OUTPUT
11026  /*printf("[DirectSound] (Duplex/Playback) physicalPlayCursorInBytes=%d, availableBytesPlayback=%d\n", physicalPlayCursorInBytes, availableBytesPlayback);*/
11027  #endif
11028 
11029  /* If there's no room available for writing we need to wait for more. */
11030  if (availableBytesPlayback == 0) {
11031  /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
11032  if (!isPlaybackDeviceStarted) {
11033  if (FAILED(ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING))) {
11034  ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
11035  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
11036  }
11037  isPlaybackDeviceStarted = MA_TRUE;
11038  } else {
11039  ma_sleep(waitTimeInMilliseconds);
11040  continue;
11041  }
11042  }
11043 
11044 
11045  /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */
11046  lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
11047  if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
11048  /* Same loop iteration. Go up to the end of the buffer. */
11049  lockSizeInBytesPlayback = (pDevice->playback.internalBufferSizeInFrames*bpfPlayback) - virtualWriteCursorInBytesPlayback;
11050  } else {
11051  /* Different loop iterations. Go up to the physical play cursor. */
11052  lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
11053  }
11054 
11055  hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
11056  if (FAILED(hr)) {
11057  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
11058  break;
11059  }
11060 
11061  /*
11062  Experiment: If the playback buffer is being starved, pad it with some silence to get it back in sync. This will cause a glitch, but it may prevent
11063  endless glitching due to it constantly running out of data.
11064  */
11065  if (isPlaybackDeviceStarted) {
11066  DWORD bytesQueuedForPlayback = (pDevice->playback.internalBufferSizeInFrames*bpfPlayback) - availableBytesPlayback;
11067  if (bytesQueuedForPlayback < ((pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)*bpfPlayback)) {
11068  silentPaddingInBytes = ((pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)*2*bpfPlayback) - bytesQueuedForPlayback;
11069  if (silentPaddingInBytes > lockSizeInBytesPlayback) {
11070  silentPaddingInBytes = lockSizeInBytesPlayback;
11071  }
11072 
11073  #ifdef MA_DEBUG_OUTPUT
11074  printf("[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%d, silentPaddingInBytes=%d\n", availableBytesPlayback, silentPaddingInBytes);
11075  #endif
11076  }
11077  }
11078 
11079  /* At this point we have a buffer for output. */
11080  if (silentPaddingInBytes > 0) {
11081  ma_zero_memory(pMappedBufferPlayback, silentPaddingInBytes);
11082  framesWrittenThisIteration = silentPaddingInBytes/bpfPlayback;
11083  } else {
11084  framesWrittenThisIteration = (ma_uint32)ma_pcm_converter_read(&pDevice->playback.converter, pMappedBufferPlayback, mappedSizeInBytesPlayback/bpfPlayback);
11085  }
11086 
11087 
11088  hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedBufferPlayback, framesWrittenThisIteration*bpfPlayback, NULL, 0);
11089  if (FAILED(hr)) {
11090  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
11091  break;
11092  }
11093 
11094  virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfPlayback;
11095  if ((virtualWriteCursorInBytesPlayback/bpfPlayback) == pDevice->playback.internalBufferSizeInFrames) {
11096  virtualWriteCursorInBytesPlayback = 0;
11097  virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
11098  }
11099 
11100  /*
11101  We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds
11102  a bit of a buffer to prevent the playback buffer from getting starved.
11103  */
11104  framesWrittenToPlaybackDevice += framesWrittenThisIteration;
11105  if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= ((pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)*2)) {
11106  if (FAILED(ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING))) {
11107  ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
11108  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
11109  }
11110  isPlaybackDeviceStarted = MA_TRUE;
11111  }
11112 
11113  if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfPlayback) {
11114  break; /* We're finished with the output data.*/
11115  }
11116  }
11117 
11118  if (inputFramesInExternalFormatCount < inputFramesInExternalFormatCap) {
11119  break; /* We just consumed every input sample. */
11120  }
11121  }
11122 
11123 
11124  /* At this point we're done with the mapped portion of the capture buffer. */
11125  hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedBufferCapture, mappedSizeInBytesCapture, NULL, 0);
11126  if (FAILED(hr)) {
11127  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
11128  }
11129  prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture);
11130  } break;
11131 
11132 
11133 
11135  {
11136  DWORD physicalCaptureCursorInBytes;
11137  DWORD physicalReadCursorInBytes;
11138  if (FAILED(ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes))) {
11139  return MA_ERROR;
11140  }
11141 
11142  /* If the previous capture position is the same as the current position we need to wait a bit longer. */
11143  if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) {
11144  ma_sleep(waitTimeInMilliseconds);
11145  continue;
11146  }
11147 
11148  /* Getting here means we have capture data available. */
11149  if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
11150  /* The capture position has not looped. This is the simple case. */
11151  lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
11152  lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
11153  } else {
11154  /*
11155  The capture position has looped. This is the more complex case. Map to the end of the buffer. If this does not return anything,
11156  do it again from the start.
11157  */
11158  if (prevReadCursorInBytesCapture < pDevice->capture.internalBufferSizeInFrames*bpfCapture) {
11159  /* Lock up to the end of the buffer. */
11160  lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
11161  lockSizeInBytesCapture = (pDevice->capture.internalBufferSizeInFrames*bpfCapture) - prevReadCursorInBytesCapture;
11162  } else {
11163  /* Lock starting from the start of the buffer. */
11164  lockOffsetInBytesCapture = 0;
11165  lockSizeInBytesCapture = physicalReadCursorInBytes;
11166  }
11167  }
11168 
11169  #ifdef MA_DEBUG_OUTPUT
11170  /*printf("[DirectSound] (Capture) physicalCaptureCursorInBytes=%d, physicalReadCursorInBytes=%d\n", physicalCaptureCursorInBytes, physicalReadCursorInBytes);*/
11171  /*printf("[DirectSound] (Capture) lockOffsetInBytesCapture=%d, lockSizeInBytesCapture=%d\n", lockOffsetInBytesCapture, lockSizeInBytesCapture);*/
11172  #endif
11173 
11174  if (lockSizeInBytesCapture < (pDevice->capture.internalBufferSizeInFrames/pDevice->capture.internalPeriods)) {
11175  ma_sleep(waitTimeInMilliseconds);
11176  continue; /* Nothing is available in the capture buffer. */
11177  }
11178 
11179  hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
11180  if (FAILED(hr)) {
11181  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
11182  }
11183 
11184  #ifdef MA_DEBUG_OUTPUT
11185  if (lockSizeInBytesCapture != mappedSizeInBytesCapture) {
11186  printf("[DirectSound] (Capture) lockSizeInBytesCapture=%d != mappedSizeInBytesCapture=%d\n", lockSizeInBytesCapture, mappedSizeInBytesCapture);
11187  }
11188  #endif
11189 
11190  ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfCapture, pMappedBufferCapture);
11191 
11192  hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedBufferCapture, mappedSizeInBytesCapture, NULL, 0);
11193  if (FAILED(hr)) {
11194  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
11195  }
11196  prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture;
11197 
11198  if (prevReadCursorInBytesCapture == (pDevice->capture.internalBufferSizeInFrames*bpfCapture)) {
11199  prevReadCursorInBytesCapture = 0;
11200  }
11201  } break;
11202 
11203 
11204 
11206  {
11207  DWORD availableBytesPlayback;
11208  DWORD physicalPlayCursorInBytes;
11209  DWORD physicalWriteCursorInBytes;
11210  if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
11211  break;
11212  }
11213 
11214  if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
11215  physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
11216  }
11217  prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
11218 
11219  /* If there's any bytes available for writing we can do that now. The space between the virtual cursor position and play cursor. */
11220  if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
11221  /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
11222  if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
11223  availableBytesPlayback = (pDevice->playback.internalBufferSizeInFrames*bpfPlayback) - virtualWriteCursorInBytesPlayback;
11224  availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
11225  } else {
11226  /* This is an error. */
11227  #ifdef MA_DEBUG_OUTPUT
11228  printf("[DirectSound] (Playback) WARNING: Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%d, virtualWriteCursorInBytes=%d.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
11229  #endif
11230  availableBytesPlayback = 0;
11231  }
11232  } else {
11233  /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
11234  if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
11235  availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
11236  } else {
11237  /* This is an error. */
11238  #ifdef MA_DEBUG_OUTPUT
11239  printf("[DirectSound] (Playback) WARNING: Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%d, virtualWriteCursorInBytes=%d.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
11240  #endif
11241  availableBytesPlayback = 0;
11242  }
11243  }
11244 
11245  #ifdef MA_DEBUG_OUTPUT
11246  /*printf("[DirectSound] (Playback) physicalPlayCursorInBytes=%d, availableBytesPlayback=%d\n", physicalPlayCursorInBytes, availableBytesPlayback);*/
11247  #endif
11248 
11249  /* If there's no room available for writing we need to wait for more. */
11250  if (availableBytesPlayback < (pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)) {
11251  /* If we haven't started the device yet, this will never get beyond 0. In this case we need to get the device started. */
11252  if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) {
11253  if (FAILED(ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING))) {
11254  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
11255  }
11256  isPlaybackDeviceStarted = MA_TRUE;
11257  } else {
11258  ma_sleep(waitTimeInMilliseconds);
11259  continue;
11260  }
11261  }
11262 
11263  /* Getting here means there room available somewhere. We limit this to either the end of the buffer or the physical play cursor, whichever is closest. */
11264  lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
11265  if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
11266  /* Same loop iteration. Go up to the end of the buffer. */
11267  lockSizeInBytesPlayback = (pDevice->playback.internalBufferSizeInFrames*bpfPlayback) - virtualWriteCursorInBytesPlayback;
11268  } else {
11269  /* Different loop iterations. Go up to the physical play cursor. */
11270  lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
11271  }
11272 
11273  hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
11274  if (FAILED(hr)) {
11275  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.", MA_FAILED_TO_MAP_DEVICE_BUFFER);
11276  break;
11277  }
11278 
11279  /* At this point we have a buffer for output. */
11280  ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfPlayback), pMappedBufferPlayback);
11281 
11282  hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedBufferPlayback, mappedSizeInBytesPlayback, NULL, 0);
11283  if (FAILED(hr)) {
11284  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.", MA_FAILED_TO_UNMAP_DEVICE_BUFFER);
11285  break;
11286  }
11287 
11288  virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback;
11289  if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalBufferSizeInFrames*bpfPlayback) {
11290  virtualWriteCursorInBytesPlayback = 0;
11291  virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
11292  }
11293 
11294  /*
11295  We may need to start the device. We want two full periods to be written before starting the playback device. Having an extra period adds
11296  a bit of a buffer to prevent the playback buffer from getting starved.
11297  */
11298  framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfPlayback;
11299  if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods)) {
11300  if (FAILED(ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING))) {
11301  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.", MA_FAILED_TO_START_BACKEND_DEVICE);
11302  }
11303  isPlaybackDeviceStarted = MA_TRUE;
11304  }
11305  } break;
11306 
11307 
11308  default: return MA_INVALID_ARGS; /* Invalid device type. */
11309  }
11310 
11311  if (result != MA_SUCCESS) {
11312  return result;
11313  }
11314  }
11315 
11316  /* Getting here means the device is being stopped. */
11317  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
11318  if (FAILED(ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer))) {
11319  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
11320  }
11321  }
11322 
11323  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
11324  /* The playback device should be drained before stopping. All we do is wait until the available bytes is equal to the size of the buffer. */
11325  if (isPlaybackDeviceStarted) {
11326  for (;;) {
11327  DWORD availableBytesPlayback = 0;
11328  DWORD physicalPlayCursorInBytes;
11329  DWORD physicalWriteCursorInBytes;
11330  if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
11331  break;
11332  }
11333 
11334  if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
11335  physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
11336  }
11337  prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
11338 
11339  if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
11340  /* Same loop iteration. The available bytes wraps all the way around from the virtual write cursor to the physical play cursor. */
11341  if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
11342  availableBytesPlayback = (pDevice->playback.internalBufferSizeInFrames*bpfPlayback) - virtualWriteCursorInBytesPlayback;
11343  availableBytesPlayback += physicalPlayCursorInBytes; /* Wrap around. */
11344  } else {
11345  break;
11346  }
11347  } else {
11348  /* Different loop iterations. The available bytes only goes from the virtual write cursor to the physical play cursor. */
11349  if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
11350  availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
11351  } else {
11352  break;
11353  }
11354  }
11355 
11356  if (availableBytesPlayback >= (pDevice->playback.internalBufferSizeInFrames*bpfPlayback)) {
11357  break;
11358  }
11359 
11360  ma_sleep(waitTimeInMilliseconds);
11361  }
11362  }
11363 
11364  if (FAILED(ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer))) {
11365  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
11366  }
11367 
11368  ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0);
11369  }
11370 
11371  return MA_SUCCESS;
11372 }
11373 
11374 ma_result ma_context_uninit__dsound(ma_context* pContext)
11375 {
11376  ma_assert(pContext != NULL);
11377  ma_assert(pContext->backend == ma_backend_dsound);
11378 
11379  ma_dlclose(pContext, pContext->dsound.hDSoundDLL);
11380 
11381  return MA_SUCCESS;
11382 }
11383 
11384 ma_result ma_context_init__dsound(const ma_context_config* pConfig, ma_context* pContext)
11385 {
11386  ma_assert(pContext != NULL);
11387 
11388  (void)pConfig;
11389 
11390  pContext->dsound.hDSoundDLL = ma_dlopen(pContext, "dsound.dll");
11391  if (pContext->dsound.hDSoundDLL == NULL) {
11392  return MA_API_NOT_FOUND;
11393  }
11394 
11395  pContext->dsound.DirectSoundCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCreate");
11396  pContext->dsound.DirectSoundEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA");
11397  pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate");
11398  pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA");
11399 
11400  pContext->onUninit = ma_context_uninit__dsound;
11401  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__dsound;
11402  pContext->onEnumDevices = ma_context_enumerate_devices__dsound;
11403  pContext->onGetDeviceInfo = ma_context_get_device_info__dsound;
11404  pContext->onDeviceInit = ma_device_init__dsound;
11405  pContext->onDeviceUninit = ma_device_uninit__dsound;
11406  pContext->onDeviceStart = NULL; /* Not used. Started in onDeviceMainLoop. */
11407  pContext->onDeviceStop = NULL; /* Not used. Stopped in onDeviceMainLoop. */
11408  pContext->onDeviceMainLoop = ma_device_main_loop__dsound;
11409 
11410  return MA_SUCCESS;
11411 }
11412 #endif
11413 
11414 
11415 
11416 /******************************************************************************
11417 
11418 WinMM Backend
11419 
11420 ******************************************************************************/
11421 #ifdef MA_HAS_WINMM
11422 
11423 /*
11424 Some older compilers don't have WAVEOUTCAPS2A and WAVEINCAPS2A, so we'll need to write this ourselves. These structures
11425 are exactly the same as the older ones but they have a few GUIDs for manufacturer/product/name identification. I'm keeping
11426 the names the same as the Win32 library for consistency, but namespaced to avoid naming conflicts with the Win32 version.
11427 */
11428 typedef struct
11429 {
11430  WORD wMid;
11431  WORD wPid;
11432  MMVERSION vDriverVersion;
11433  CHAR szPname[MAXPNAMELEN];
11434  DWORD dwFormats;
11435  WORD wChannels;
11436  WORD wReserved1;
11437  DWORD dwSupport;
11438  GUID ManufacturerGuid;
11439  GUID ProductGuid;
11440  GUID NameGuid;
11441 } MA_WAVEOUTCAPS2A;
11442 typedef struct
11443 {
11444  WORD wMid;
11445  WORD wPid;
11446  MMVERSION vDriverVersion;
11447  CHAR szPname[MAXPNAMELEN];
11448  DWORD dwFormats;
11449  WORD wChannels;
11450  WORD wReserved1;
11451  GUID ManufacturerGuid;
11452  GUID ProductGuid;
11453  GUID NameGuid;
11454 } MA_WAVEINCAPS2A;
11455 
11456 typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void);
11457 typedef MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc);
11458 typedef MMRESULT (WINAPI * MA_PFN_waveOutOpen)(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
11459 typedef MMRESULT (WINAPI * MA_PFN_waveOutClose)(HWAVEOUT hwo);
11460 typedef MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
11461 typedef MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
11462 typedef MMRESULT (WINAPI * MA_PFN_waveOutWrite)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh);
11463 typedef MMRESULT (WINAPI * MA_PFN_waveOutReset)(HWAVEOUT hwo);
11464 typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void);
11465 typedef MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic);
11466 typedef MMRESULT (WINAPI * MA_PFN_waveInOpen)(LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
11467 typedef MMRESULT (WINAPI * MA_PFN_waveInClose)(HWAVEIN hwi);
11468 typedef MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
11469 typedef MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
11470 typedef MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh);
11471 typedef MMRESULT (WINAPI * MA_PFN_waveInStart)(HWAVEIN hwi);
11472 typedef MMRESULT (WINAPI * MA_PFN_waveInReset)(HWAVEIN hwi);
11473 
11474 ma_result ma_result_from_MMRESULT(MMRESULT resultMM)
11475 {
11476  switch (resultMM) {
11477  case MMSYSERR_NOERROR: return MA_SUCCESS;
11478  case MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS;
11479  case MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS;
11480  case MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY;
11481  case MMSYSERR_INVALFLAG: return MA_INVALID_ARGS;
11482  case MMSYSERR_INVALPARAM: return MA_INVALID_ARGS;
11483  case MMSYSERR_HANDLEBUSY: return MA_DEVICE_BUSY;
11484  case MMSYSERR_ERROR: return MA_ERROR;
11485  default: return MA_ERROR;
11486  }
11487 }
11488 
11489 char* ma_find_last_character(char* str, char ch)
11490 {
11491  char* last;
11492 
11493  if (str == NULL) {
11494  return NULL;
11495  }
11496 
11497  last = NULL;
11498  while (*str != '\0') {
11499  if (*str == ch) {
11500  last = str;
11501  }
11502 
11503  str += 1;
11504  }
11505 
11506  return last;
11507 }
11508 
11509 
11510 /*
11511 Our own "WAVECAPS" structure that contains generic information shared between WAVEOUTCAPS2 and WAVEINCAPS2 so
11512 we can do things generically and typesafely. Names are being kept the same for consistency.
11513 */
11514 typedef struct
11515 {
11516  CHAR szPname[MAXPNAMELEN];
11517  DWORD dwFormats;
11518  WORD wChannels;
11519  GUID NameGuid;
11520 } MA_WAVECAPSA;
11521 
11522 ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate)
11523 {
11524  WORD bitsPerSample = 0;
11525  DWORD sampleRate = 0;
11526 
11527  if (pBitsPerSample) {
11528  *pBitsPerSample = 0;
11529  }
11530  if (pSampleRate) {
11531  *pSampleRate = 0;
11532  }
11533 
11534  if (channels == 1) {
11535  bitsPerSample = 16;
11536  if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
11537  sampleRate = 48000;
11538  } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
11539  sampleRate = 44100;
11540  } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
11541  sampleRate = 22050;
11542  } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
11543  sampleRate = 11025;
11544  } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
11545  sampleRate = 96000;
11546  } else {
11547  bitsPerSample = 8;
11548  if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
11549  sampleRate = 48000;
11550  } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
11551  sampleRate = 44100;
11552  } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
11553  sampleRate = 22050;
11554  } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
11555  sampleRate = 11025;
11556  } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
11557  sampleRate = 96000;
11558  } else {
11559  return MA_FORMAT_NOT_SUPPORTED;
11560  }
11561  }
11562  } else {
11563  bitsPerSample = 16;
11564  if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
11565  sampleRate = 48000;
11566  } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
11567  sampleRate = 44100;
11568  } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
11569  sampleRate = 22050;
11570  } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
11571  sampleRate = 11025;
11572  } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
11573  sampleRate = 96000;
11574  } else {
11575  bitsPerSample = 8;
11576  if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
11577  sampleRate = 48000;
11578  } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
11579  sampleRate = 44100;
11580  } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
11581  sampleRate = 22050;
11582  } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
11583  sampleRate = 11025;
11584  } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
11585  sampleRate = 96000;
11586  } else {
11587  return MA_FORMAT_NOT_SUPPORTED;
11588  }
11589  }
11590  }
11591 
11592  if (pBitsPerSample) {
11593  *pBitsPerSample = bitsPerSample;
11594  }
11595  if (pSampleRate) {
11596  *pSampleRate = sampleRate;
11597  }
11598 
11599  return MA_SUCCESS;
11600 }
11601 
11602 ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, WAVEFORMATEX* pWF)
11603 {
11604  ma_assert(pWF != NULL);
11605 
11606  ma_zero_object(pWF);
11607  pWF->cbSize = sizeof(*pWF);
11608  pWF->wFormatTag = WAVE_FORMAT_PCM;
11609  pWF->nChannels = (WORD)channels;
11610  if (pWF->nChannels > 2) {
11611  pWF->nChannels = 2;
11612  }
11613 
11614  if (channels == 1) {
11615  pWF->wBitsPerSample = 16;
11616  if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
11617  pWF->nSamplesPerSec = 48000;
11618  } else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
11619  pWF->nSamplesPerSec = 44100;
11620  } else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
11621  pWF->nSamplesPerSec = 22050;
11622  } else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
11623  pWF->nSamplesPerSec = 11025;
11624  } else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
11625  pWF->nSamplesPerSec = 96000;
11626  } else {
11627  pWF->wBitsPerSample = 8;
11628  if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
11629  pWF->nSamplesPerSec = 48000;
11630  } else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
11631  pWF->nSamplesPerSec = 44100;
11632  } else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
11633  pWF->nSamplesPerSec = 22050;
11634  } else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
11635  pWF->nSamplesPerSec = 11025;
11636  } else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
11637  pWF->nSamplesPerSec = 96000;
11638  } else {
11639  return MA_FORMAT_NOT_SUPPORTED;
11640  }
11641  }
11642  } else {
11643  pWF->wBitsPerSample = 16;
11644  if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
11645  pWF->nSamplesPerSec = 48000;
11646  } else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
11647  pWF->nSamplesPerSec = 44100;
11648  } else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
11649  pWF->nSamplesPerSec = 22050;
11650  } else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
11651  pWF->nSamplesPerSec = 11025;
11652  } else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
11653  pWF->nSamplesPerSec = 96000;
11654  } else {
11655  pWF->wBitsPerSample = 8;
11656  if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
11657  pWF->nSamplesPerSec = 48000;
11658  } else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
11659  pWF->nSamplesPerSec = 44100;
11660  } else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
11661  pWF->nSamplesPerSec = 22050;
11662  } else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
11663  pWF->nSamplesPerSec = 11025;
11664  } else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
11665  pWF->nSamplesPerSec = 96000;
11666  } else {
11667  return MA_FORMAT_NOT_SUPPORTED;
11668  }
11669  }
11670  }
11671 
11672  pWF->nBlockAlign = (pWF->nChannels * pWF->wBitsPerSample) / 8;
11673  pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;
11674 
11675  return MA_SUCCESS;
11676 }
11677 
11678 ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo)
11679 {
11680  WORD bitsPerSample;
11681  DWORD sampleRate;
11682  ma_result result;
11683 
11684  ma_assert(pContext != NULL);
11685  ma_assert(pCaps != NULL);
11686  ma_assert(pDeviceInfo != NULL);
11687 
11688  /*
11689  Name / Description
11690 
11691  Unfortunately the name specified in WAVE(OUT/IN)CAPS2 is limited to 31 characters. This results in an unprofessional looking
11692  situation where the names of the devices are truncated. To help work around this, we need to look at the name GUID and try
11693  looking in the registry for the full name. If we can't find it there, we need to just fall back to the default name.
11694  */
11695 
11696  /* Set the default to begin with. */
11697  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1);
11698 
11699  /*
11700  Now try the registry. There's a few things to consider here:
11701  - The name GUID can be null, in which we case we just need to stick to the original 31 characters.
11702  - If the name GUID is not present in the registry we'll also need to stick to the original 31 characters.
11703  - I like consistency, so I want the returned device names to be consistent with those returned by WASAPI and DirectSound. The
11704  problem, however is that WASAPI and DirectSound use "<component> (<name>)" format (such as "Speakers (High Definition Audio)"),
11705  but WinMM does not specificy the component name. From my admittedly limited testing, I've notice the component name seems to
11706  usually fit within the 31 characters of the fixed sized buffer, so what I'm going to do is parse that string for the component
11707  name, and then concatenate the name from the registry.
11708  */
11709  if (!ma_is_guid_equal(&pCaps->NameGuid, &MA_GUID_NULL)) {
11710  wchar_t guidStrW[256];
11711  if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) {
11712  char guidStr[256];
11713  char keyStr[1024];
11714  HKEY hKey;
11715 
11716  WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE);
11717 
11718  ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\");
11719  ma_strcat_s(keyStr, sizeof(keyStr), guidStr);
11720 
11721  if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
11722  BYTE nameFromReg[512];
11723  DWORD nameFromRegSize = sizeof(nameFromReg);
11724  result = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize);
11725  ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey);
11726 
11727  if (result == ERROR_SUCCESS) {
11728  /* We have the value from the registry, so now we need to construct the name string. */
11729  char name[1024];
11730  if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) {
11731  char* nameBeg = ma_find_last_character(name, '(');
11732  if (nameBeg != NULL) {
11733  size_t leadingLen = (nameBeg - name);
11734  ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1);
11735 
11736  /* The closing ")", if it can fit. */
11737  if (leadingLen + nameFromRegSize < sizeof(name)-1) {
11738  ma_strcat_s(name, sizeof(name), ")");
11739  }
11740 
11741  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1);
11742  }
11743  }
11744  }
11745  }
11746  }
11747  }
11748 
11749 
11750  result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate);
11751  if (result != MA_SUCCESS) {
11752  return result;
11753  }
11754 
11755  pDeviceInfo->minChannels = pCaps->wChannels;
11756  pDeviceInfo->maxChannels = pCaps->wChannels;
11757  pDeviceInfo->minSampleRate = sampleRate;
11758  pDeviceInfo->maxSampleRate = sampleRate;
11759  pDeviceInfo->formatCount = 1;
11760  if (bitsPerSample == 8) {
11761  pDeviceInfo->formats[0] = ma_format_u8;
11762  } else if (bitsPerSample == 16) {
11763  pDeviceInfo->formats[0] = ma_format_s16;
11764  } else if (bitsPerSample == 24) {
11765  pDeviceInfo->formats[0] = ma_format_s24;
11766  } else if (bitsPerSample == 32) {
11767  pDeviceInfo->formats[0] = ma_format_s32;
11768  } else {
11769  return MA_FORMAT_NOT_SUPPORTED;
11770  }
11771 
11772  return MA_SUCCESS;
11773 }
11774 
11775 ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo)
11776 {
11777  MA_WAVECAPSA caps;
11778 
11779  ma_assert(pContext != NULL);
11780  ma_assert(pCaps != NULL);
11781  ma_assert(pDeviceInfo != NULL);
11782 
11783  ma_copy_memory(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
11784  caps.dwFormats = pCaps->dwFormats;
11785  caps.wChannels = pCaps->wChannels;
11786  caps.NameGuid = pCaps->NameGuid;
11787  return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
11788 }
11789 
11790 ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo)
11791 {
11792  MA_WAVECAPSA caps;
11793 
11794  ma_assert(pContext != NULL);
11795  ma_assert(pCaps != NULL);
11796  ma_assert(pDeviceInfo != NULL);
11797 
11798  ma_copy_memory(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
11799  caps.dwFormats = pCaps->dwFormats;
11800  caps.wChannels = pCaps->wChannels;
11801  caps.NameGuid = pCaps->NameGuid;
11802  return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
11803 }
11804 
11805 
11806 ma_bool32 ma_context_is_device_id_equal__winmm(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
11807 {
11808  ma_assert(pContext != NULL);
11809  ma_assert(pID0 != NULL);
11810  ma_assert(pID1 != NULL);
11811  (void)pContext;
11812 
11813  return pID0->winmm == pID1->winmm;
11814 }
11815 
11816 ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
11817 {
11818  UINT playbackDeviceCount;
11819  UINT captureDeviceCount;
11820  UINT iPlaybackDevice;
11821  UINT iCaptureDevice;
11822 
11823  ma_assert(pContext != NULL);
11824  ma_assert(callback != NULL);
11825 
11826  /* Playback. */
11827  playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)();
11828  for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) {
11829  MMRESULT result;
11830  MA_WAVEOUTCAPS2A caps;
11831 
11832  ma_zero_object(&caps);
11833 
11834  result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (WAVEOUTCAPSA*)&caps, sizeof(caps));
11835  if (result == MMSYSERR_NOERROR) {
11836  ma_device_info deviceInfo;
11837 
11838  ma_zero_object(&deviceInfo);
11839  deviceInfo.id.winmm = iPlaybackDevice;
11840 
11841  if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
11842  ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
11843  if (cbResult == MA_FALSE) {
11844  return MA_SUCCESS; /* Enumeration was stopped. */
11845  }
11846  }
11847  }
11848  }
11849 
11850  /* Capture. */
11851  captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)();
11852  for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) {
11853  MMRESULT result;
11854  MA_WAVEINCAPS2A caps;
11855 
11856  ma_zero_object(&caps);
11857 
11858  result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (WAVEINCAPSA*)&caps, sizeof(caps));
11859  if (result == MMSYSERR_NOERROR) {
11860  ma_device_info deviceInfo;
11861 
11862  ma_zero_object(&deviceInfo);
11863  deviceInfo.id.winmm = iCaptureDevice;
11864 
11865  if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
11866  ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
11867  if (cbResult == MA_FALSE) {
11868  return MA_SUCCESS; /* Enumeration was stopped. */
11869  }
11870  }
11871  }
11872  }
11873 
11874  return MA_SUCCESS;
11875 }
11876 
11877 ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
11878 {
11879  UINT winMMDeviceID;
11880 
11881  ma_assert(pContext != NULL);
11882 
11883  if (shareMode == ma_share_mode_exclusive) {
11885  }
11886 
11887  winMMDeviceID = 0;
11888  if (pDeviceID != NULL) {
11889  winMMDeviceID = (UINT)pDeviceID->winmm;
11890  }
11891 
11892  pDeviceInfo->id.winmm = winMMDeviceID;
11893 
11894  if (deviceType == ma_device_type_playback) {
11895  MMRESULT result;
11896  MA_WAVEOUTCAPS2A caps;
11897 
11898  ma_zero_object(&caps);
11899 
11900  result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (WAVEOUTCAPSA*)&caps, sizeof(caps));
11901  if (result == MMSYSERR_NOERROR) {
11902  return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo);
11903  }
11904  } else {
11905  MMRESULT result;
11906  MA_WAVEINCAPS2A caps;
11907 
11908  ma_zero_object(&caps);
11909 
11910  result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (WAVEINCAPSA*)&caps, sizeof(caps));
11911  if (result == MMSYSERR_NOERROR) {
11912  return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo);
11913  }
11914  }
11915 
11916  return MA_NO_DEVICE;
11917 }
11918 
11919 
11920 void ma_device_uninit__winmm(ma_device* pDevice)
11921 {
11922  ma_assert(pDevice != NULL);
11923 
11924  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
11925  ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture);
11926  CloseHandle((HANDLE)pDevice->winmm.hEventCapture);
11927  }
11928 
11929  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
11930  ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
11931  ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
11932  CloseHandle((HANDLE)pDevice->winmm.hEventPlayback);
11933  }
11934 
11935  ma_free(pDevice->winmm._pHeapData);
11936 
11937  ma_zero_object(&pDevice->winmm); /* Safety. */
11938 }
11939 
11940 ma_result ma_device_init__winmm(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
11941 {
11942  const char* errorMsg = "";
11943  ma_result errorCode = MA_ERROR;
11945  ma_uint32 heapSize;
11946  UINT winMMDeviceIDPlayback = 0;
11947  UINT winMMDeviceIDCapture = 0;
11948  ma_uint32 bufferSizeInMilliseconds;
11949 
11950  ma_assert(pDevice != NULL);
11951  ma_zero_object(&pDevice->winmm);
11952 
11953  if (pConfig->deviceType == ma_device_type_loopback) {
11955  }
11956 
11957  /* No exlusive mode with WinMM. */
11961  }
11962 
11963  bufferSizeInMilliseconds = pConfig->bufferSizeInMilliseconds;
11964  if (bufferSizeInMilliseconds == 0) {
11965  bufferSizeInMilliseconds = ma_calculate_buffer_size_in_milliseconds_from_frames(pConfig->bufferSizeInFrames, pConfig->sampleRate);
11966  }
11967 
11968  /* WinMM has horrible latency. */
11969  if (pDevice->usingDefaultBufferSize) {
11971  bufferSizeInMilliseconds = 40 * pConfig->periods;
11972  } else {
11973  bufferSizeInMilliseconds = 400 * pConfig->periods;
11974  }
11975  }
11976 
11977 
11978  if (pConfig->playback.pDeviceID != NULL) {
11979  winMMDeviceIDPlayback = (UINT)pConfig->playback.pDeviceID->winmm;
11980  }
11981  if (pConfig->capture.pDeviceID != NULL) {
11982  winMMDeviceIDCapture = (UINT)pConfig->capture.pDeviceID->winmm;
11983  }
11984 
11985  /* The capture device needs to be initialized first. */
11986  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
11987  WAVEINCAPSA caps;
11988  WAVEFORMATEX wf;
11989  MMRESULT resultMM;
11990 
11991  /* We use an event to know when a new fragment needs to be enqueued. */
11992  pDevice->winmm.hEventCapture = (ma_handle)CreateEvent(NULL, TRUE, TRUE, NULL);
11993  if (pDevice->winmm.hEventCapture == NULL) {
11994  errorMsg = "[WinMM] Failed to create event for fragment enqueing for the capture device.", errorCode = MA_FAILED_TO_CREATE_EVENT;
11995  goto on_error;
11996  }
11997 
11998  /* The format should be based on the device's actual format. */
11999  if (((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
12000  errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
12001  goto on_error;
12002  }
12003 
12004  result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
12005  if (result != MA_SUCCESS) {
12006  errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
12007  goto on_error;
12008  }
12009 
12010  resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((LPHWAVEIN)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC);
12011  if (resultMM != MMSYSERR_NOERROR) {
12012  errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
12013  goto on_error;
12014  }
12015 
12016  pDevice->capture.internalFormat = ma_format_from_WAVEFORMATEX(&wf);
12017  pDevice->capture.internalChannels = wf.nChannels;
12018  pDevice->capture.internalSampleRate = wf.nSamplesPerSec;
12019  ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
12020  pDevice->capture.internalPeriods = pConfig->periods;
12021  pDevice->capture.internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(bufferSizeInMilliseconds, pDevice->capture.internalSampleRate);
12022  }
12023 
12024  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
12025  WAVEOUTCAPSA caps;
12026  WAVEFORMATEX wf;
12027  MMRESULT resultMM;
12028 
12029  /* We use an event to know when a new fragment needs to be enqueued. */
12030  pDevice->winmm.hEventPlayback = (ma_handle)CreateEvent(NULL, TRUE, TRUE, NULL);
12031  if (pDevice->winmm.hEventPlayback == NULL) {
12032  errorMsg = "[WinMM] Failed to create event for fragment enqueing for the playback device.", errorCode = MA_FAILED_TO_CREATE_EVENT;
12033  goto on_error;
12034  }
12035 
12036  /* The format should be based on the device's actual format. */
12037  if (((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MMSYSERR_NOERROR) {
12038  errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
12039  goto on_error;
12040  }
12041 
12042  result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
12043  if (result != MA_SUCCESS) {
12044  errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
12045  goto on_error;
12046  }
12047 
12048  resultMM = ((MA_PFN_waveOutOpen)pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC);
12049  if (resultMM != MMSYSERR_NOERROR) {
12050  errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
12051  goto on_error;
12052  }
12053 
12054  pDevice->playback.internalFormat = ma_format_from_WAVEFORMATEX(&wf);
12055  pDevice->playback.internalChannels = wf.nChannels;
12056  pDevice->playback.internalSampleRate = wf.nSamplesPerSec;
12057  ma_get_standard_channel_map(ma_standard_channel_map_microsoft, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
12058  pDevice->playback.internalPeriods = pConfig->periods;
12059  pDevice->playback.internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(bufferSizeInMilliseconds, pDevice->playback.internalSampleRate);
12060  }
12061 
12062  /*
12063  The heap allocated data is allocated like so:
12064 
12065  [Capture WAVEHDRs][Playback WAVEHDRs][Capture Intermediary Buffer][Playback Intermediary Buffer]
12066  */
12067  heapSize = 0;
12068  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
12069  heapSize += sizeof(WAVEHDR)*pDevice->capture.internalPeriods + (pDevice->capture.internalBufferSizeInFrames*ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
12070  }
12071  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
12072  heapSize += sizeof(WAVEHDR)*pDevice->playback.internalPeriods + (pDevice->playback.internalBufferSizeInFrames*ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
12073  }
12074 
12075  pDevice->winmm._pHeapData = (ma_uint8*)ma_malloc(heapSize);
12076  if (pDevice->winmm._pHeapData == NULL) {
12077  errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY;
12078  goto on_error;
12079  }
12080 
12081  ma_zero_memory(pDevice->winmm._pHeapData, heapSize);
12082 
12083  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
12084  ma_uint32 iPeriod;
12085 
12086  if (pConfig->deviceType == ma_device_type_capture) {
12087  pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
12088  pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods));
12089  } else {
12090  pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
12091  pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods + pDevice->playback.internalPeriods));
12092  }
12093 
12094  /* Prepare headers. */
12095  for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
12096  ma_uint32 fragmentSizeInBytes = ma_get_fragment_size_in_bytes(pDevice->capture.internalBufferSizeInFrames, pDevice->capture.internalPeriods, pDevice->capture.internalFormat, pDevice->capture.internalChannels);
12097 
12098  ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferCapture + (fragmentSizeInBytes*iPeriod));
12099  ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = fragmentSizeInBytes;
12100  ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L;
12101  ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L;
12102  ((MA_PFN_waveInPrepareHeader)pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
12103 
12104  /*
12105  The user data of the WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means
12106  it's unlocked and available for writing. A value of 1 means it's locked.
12107  */
12108  ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0;
12109  }
12110  }
12111  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
12112  ma_uint32 iPeriod;
12113 
12114  if (pConfig->deviceType == ma_device_type_playback) {
12115  pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData;
12116  pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*pDevice->playback.internalPeriods);
12117  } else {
12118  pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods));
12119  pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDevice->capture.internalPeriods + pDevice->playback.internalPeriods)) + (pDevice->playback.internalBufferSizeInFrames*ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
12120  }
12121 
12122  /* Prepare headers. */
12123  for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
12124  ma_uint32 fragmentSizeInBytes = ma_get_fragment_size_in_bytes(pDevice->playback.internalBufferSizeInFrames, pDevice->playback.internalPeriods, pDevice->playback.internalFormat, pDevice->playback.internalChannels);
12125 
12126  ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferPlayback + (fragmentSizeInBytes*iPeriod));
12127  ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = fragmentSizeInBytes;
12128  ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L;
12129  ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L;
12130  ((MA_PFN_waveOutPrepareHeader)pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));
12131 
12132  /*
12133  The user data of the WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means
12134  it's unlocked and available for writing. A value of 1 means it's locked.
12135  */
12136  ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0;
12137  }
12138  }
12139 
12140  return MA_SUCCESS;
12141 
12142 on_error:
12143  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
12144  if (pDevice->winmm.pWAVEHDRCapture != NULL) {
12145  ma_uint32 iPeriod;
12146  for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
12147  ((MA_PFN_waveInUnprepareHeader)pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
12148  }
12149  }
12150 
12151  ((MA_PFN_waveInClose)pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture);
12152  }
12153 
12154  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
12155  if (pDevice->winmm.pWAVEHDRCapture != NULL) {
12156  ma_uint32 iPeriod;
12157  for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
12158  ((MA_PFN_waveOutUnprepareHeader)pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR));
12159  }
12160  }
12161 
12162  ((MA_PFN_waveOutClose)pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
12163  }
12164 
12165  ma_free(pDevice->winmm._pHeapData);
12166  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, errorMsg, errorCode);
12167 }
12168 
12169 ma_result ma_device_stop__winmm(ma_device* pDevice)
12170 {
12171  MMRESULT resultMM;
12172 
12173  ma_assert(pDevice != NULL);
12174 
12175  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
12176  if (pDevice->winmm.hDeviceCapture == NULL) {
12177  return MA_INVALID_ARGS;
12178  }
12179 
12180  resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((HWAVEIN)pDevice->winmm.hDeviceCapture);
12181  if (resultMM != MMSYSERR_NOERROR) {
12182  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset capture device.", ma_result_from_MMRESULT(resultMM));
12183  }
12184  }
12185 
12186  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
12187  if (pDevice->winmm.hDevicePlayback == NULL) {
12188  return MA_INVALID_ARGS;
12189  }
12190 
12191  resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback);
12192  if (resultMM != MMSYSERR_NOERROR) {
12193  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] WARNING: Failed to reset playback device.", ma_result_from_MMRESULT(resultMM));
12194  }
12195  }
12196 
12197  return MA_SUCCESS;
12198 }
12199 
12200 ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
12201 {
12203  MMRESULT resultMM;
12204  ma_uint32 totalFramesWritten;
12205  WAVEHDR* pWAVEHDR;
12206 
12207  ma_assert(pDevice != NULL);
12208  ma_assert(pPCMFrames != NULL);
12209 
12210  if (pFramesWritten != NULL) {
12211  *pFramesWritten = 0;
12212  }
12213 
12214  pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
12215 
12216  /* Keep processing as much data as possible. */
12217  totalFramesWritten = 0;
12218  while (totalFramesWritten < frameCount) {
12219  /* If the current header has some space available we need to write part of it. */
12220  if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) { /* 0 = unlocked. */
12221  /*
12222  This header has room in it. We copy as much of it as we can. If we end up fully consuming the buffer we need to
12223  write it out and move on to the next iteration.
12224  */
12225  ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
12226  ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback;
12227 
12228  ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten));
12229  const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf);
12230  void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf);
12231  ma_copy_memory(pDst, pSrc, framesToCopy*bpf);
12232 
12233  pDevice->winmm.headerFramesConsumedPlayback += framesToCopy;
12234  totalFramesWritten += framesToCopy;
12235 
12236  /* If we've consumed the buffer entirely we need to write it out to the device. */
12237  if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) {
12238  pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */
12239  pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
12240 
12241  /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
12242  ResetEvent((HANDLE)pDevice->winmm.hEventPlayback);
12243 
12244  /* The device will be started here. */
12245  resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(WAVEHDR));
12246  if (resultMM != MMSYSERR_NOERROR) {
12247  result = ma_result_from_MMRESULT(resultMM);
12248  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed.", result);
12249  break;
12250  }
12251 
12252  /* Make sure we move to the next header. */
12253  pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods;
12254  pDevice->winmm.headerFramesConsumedPlayback = 0;
12255  }
12256 
12257  /* If at this point we have consumed the entire input buffer we can return. */
12258  ma_assert(totalFramesWritten <= frameCount);
12259  if (totalFramesWritten == frameCount) {
12260  break;
12261  }
12262 
12263  /* Getting here means there's more to process. */
12264  continue;
12265  }
12266 
12267  /* Getting here means there isn't enough room in the buffer and we need to wait for one to become available. */
12268  if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
12269  result = MA_ERROR;
12270  break;
12271  }
12272 
12273  /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
12274  if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & WHDR_DONE) != 0) {
12275  pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */
12276  pDevice->winmm.headerFramesConsumedPlayback = 0;
12277  }
12278 
12279  /* If the device has been stopped we need to break. */
12280  if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
12281  break;
12282  }
12283  }
12284 
12285  if (pFramesWritten != NULL) {
12286  *pFramesWritten = totalFramesWritten;
12287  }
12288 
12289  return result;
12290 }
12291 
12292 ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
12293 {
12295  MMRESULT resultMM;
12296  ma_uint32 totalFramesRead;
12297  WAVEHDR* pWAVEHDR;
12298 
12299  ma_assert(pDevice != NULL);
12300  ma_assert(pPCMFrames != NULL);
12301 
12302  if (pFramesRead != NULL) {
12303  *pFramesRead = 0;
12304  }
12305 
12306  pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
12307 
12308  /* Keep processing as much data as possible. */
12309  totalFramesRead = 0;
12310  while (totalFramesRead < frameCount) {
12311  /* If the current header has some space available we need to write part of it. */
12312  if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) { /* 0 = unlocked. */
12313  /* The buffer is available for reading. If we fully consume it we need to add it back to the buffer. */
12314  ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
12315  ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture;
12316 
12317  ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead));
12318  const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf);
12319  void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf);
12320  ma_copy_memory(pDst, pSrc, framesToCopy*bpf);
12321 
12322  pDevice->winmm.headerFramesConsumedCapture += framesToCopy;
12323  totalFramesRead += framesToCopy;
12324 
12325  /* If we've consumed the buffer entirely we need to add it back to the device. */
12326  if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) {
12327  pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */
12328  pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */
12329 
12330  /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
12331  ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
12332 
12333  /* The device will be started here. */
12334  resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(WAVEHDR));
12335  if (resultMM != MMSYSERR_NOERROR) {
12336  result = ma_result_from_MMRESULT(resultMM);
12337  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed.", result);
12338  break;
12339  }
12340 
12341  /* Make sure we move to the next header. */
12342  pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods;
12343  pDevice->winmm.headerFramesConsumedCapture = 0;
12344  }
12345 
12346  /* If at this point we have filled the entire input buffer we can return. */
12347  ma_assert(totalFramesRead <= frameCount);
12348  if (totalFramesRead == frameCount) {
12349  break;
12350  }
12351 
12352  /* Getting here means there's more to process. */
12353  continue;
12354  }
12355 
12356  /* Getting here means there isn't enough any data left to send to the client which means we need to wait for more. */
12357  if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
12358  result = MA_ERROR;
12359  break;
12360  }
12361 
12362  /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */
12363  if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & WHDR_DONE) != 0) {
12364  pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */
12365  pDevice->winmm.headerFramesConsumedCapture = 0;
12366  }
12367 
12368  /* If the device has been stopped we need to break. */
12369  if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
12370  break;
12371  }
12372  }
12373 
12374  if (pFramesRead != NULL) {
12375  *pFramesRead = totalFramesRead;
12376  }
12377 
12378  return result;
12379 }
12380 
12381 ma_result ma_device_main_loop__winmm(ma_device* pDevice)
12382 {
12384  ma_bool32 exitLoop = MA_FALSE;
12385 
12386  ma_assert(pDevice != NULL);
12387 
12388  /* The capture device needs to be started immediately. */
12389  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
12390  MMRESULT resultMM;
12391  WAVEHDR* pWAVEHDR;
12392  ma_uint32 iPeriod;
12393 
12394  pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
12395 
12396  /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */
12397  ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
12398 
12399  /* To start the device we attach all of the buffers and then start it. As the buffers are filled with data we will get notifications. */
12400  for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
12401  resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR));
12402  if (resultMM != MMSYSERR_NOERROR) {
12403  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture.", ma_result_from_MMRESULT(resultMM));
12404  }
12405 
12406  /* Make sure all of the buffers start out locked. We don't want to access them until the backend tells us we can. */
12407  pWAVEHDR[iPeriod].dwUser = 1; /* 1 = locked. */
12408  }
12409 
12410  /* Capture devices need to be explicitly started, unlike playback devices. */
12411  resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((HWAVEIN)pDevice->winmm.hDeviceCapture);
12412  if (resultMM != MMSYSERR_NOERROR) {
12413  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device.", ma_result_from_MMRESULT(resultMM));
12414  }
12415  }
12416 
12417 
12418  while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
12419  switch (pDevice->type)
12420  {
12421  case ma_device_type_duplex:
12422  {
12423  /* The process is: device_read -> convert -> callback -> convert -> device_write */
12424  ma_uint8 capturedDeviceData[8192];
12425  ma_uint8 playbackDeviceData[8192];
12426  ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
12427  ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
12428 
12429  ma_uint32 totalFramesProcessed = 0;
12430  ma_uint32 periodSizeInFrames = ma_min(pDevice->capture.internalBufferSizeInFrames/pDevice->capture.internalPeriods, pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods);
12431 
12432  while (totalFramesProcessed < periodSizeInFrames) {
12433  ma_uint32 framesRemaining = periodSizeInFrames - totalFramesProcessed;
12434  ma_uint32 framesProcessed;
12435  ma_uint32 framesToProcess = framesRemaining;
12436  if (framesToProcess > capturedDeviceDataCapInFrames) {
12437  framesToProcess = capturedDeviceDataCapInFrames;
12438  }
12439 
12440  result = ma_device_read__winmm(pDevice, capturedDeviceData, framesToProcess, &framesProcessed);
12441  if (result != MA_SUCCESS) {
12442  exitLoop = MA_TRUE;
12443  break;
12444  }
12445 
12446  pDevice->capture._dspFrameCount = framesToProcess;
12447  pDevice->capture._dspFrames = capturedDeviceData;
12448 
12449  for (;;) {
12450  ma_uint8 capturedData[8192];
12451  ma_uint8 playbackData[8192];
12452  ma_uint32 capturedDataCapInFrames = sizeof(capturedData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
12453  ma_uint32 playbackDataCapInFrames = sizeof(playbackData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
12454 
12455  ma_uint32 capturedFramesToTryProcessing = ma_min(capturedDataCapInFrames, playbackDataCapInFrames);
12456  ma_uint32 capturedFramesToProcess = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, capturedData, capturedFramesToTryProcessing);
12457  if (capturedFramesToProcess == 0) {
12458  break; /* Don't fire the data callback with zero frames. */
12459  }
12460 
12461  ma_device__on_data(pDevice, playbackData, capturedData, capturedFramesToProcess);
12462 
12463  /* At this point the playbackData buffer should be holding data that needs to be written to the device. */
12464  pDevice->playback._dspFrameCount = capturedFramesToProcess;
12465  pDevice->playback._dspFrames = playbackData;
12466  for (;;) {
12467  ma_uint32 playbackDeviceFramesCount = (ma_uint32)ma_pcm_converter_read(&pDevice->playback.converter, playbackDeviceData, playbackDeviceDataCapInFrames);
12468  if (playbackDeviceFramesCount == 0) {
12469  break;
12470  }
12471 
12472  result = ma_device_write__winmm(pDevice, playbackDeviceData, playbackDeviceFramesCount, NULL);
12473  if (result != MA_SUCCESS) {
12474  exitLoop = MA_TRUE;
12475  break;
12476  }
12477 
12478  if (playbackDeviceFramesCount < playbackDeviceDataCapInFrames) {
12479  break;
12480  }
12481  }
12482 
12483  if (capturedFramesToProcess < capturedFramesToTryProcessing) {
12484  break;
12485  }
12486 
12487  /* In case an error happened from ma_device_write2__alsa()... */
12488  if (result != MA_SUCCESS) {
12489  exitLoop = MA_TRUE;
12490  break;
12491  }
12492  }
12493 
12494  totalFramesProcessed += framesProcessed;
12495  }
12496  } break;
12497 
12499  {
12500  /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
12501  ma_uint8 intermediaryBuffer[8192];
12502  ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
12503  ma_uint32 periodSizeInFrames = pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods;
12504  ma_uint32 framesReadThisPeriod = 0;
12505  while (framesReadThisPeriod < periodSizeInFrames) {
12506  ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
12507  ma_uint32 framesProcessed;
12508  ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
12509  if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
12510  framesToReadThisIteration = intermediaryBufferSizeInFrames;
12511  }
12512 
12513  result = ma_device_read__winmm(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
12514  if (result != MA_SUCCESS) {
12515  exitLoop = MA_TRUE;
12516  break;
12517  }
12518 
12519  ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
12520 
12521  framesReadThisPeriod += framesProcessed;
12522  }
12523  } break;
12524 
12526  {
12527  /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
12528  ma_uint8 intermediaryBuffer[8192];
12529  ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
12530  ma_uint32 periodSizeInFrames = pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods;
12531  ma_uint32 framesWrittenThisPeriod = 0;
12532  while (framesWrittenThisPeriod < periodSizeInFrames) {
12533  ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
12534  ma_uint32 framesProcessed;
12535  ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
12536  if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
12537  framesToWriteThisIteration = intermediaryBufferSizeInFrames;
12538  }
12539 
12540  ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
12541 
12542  result = ma_device_write__winmm(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
12543  if (result != MA_SUCCESS) {
12544  exitLoop = MA_TRUE;
12545  break;
12546  }
12547 
12548  framesWrittenThisPeriod += framesProcessed;
12549  }
12550  } break;
12551 
12552  /* To silence a warning. Will never hit this. */
12554  default: break;
12555  }
12556  }
12557 
12558 
12559  /* Here is where the device is started. */
12560  ma_device_stop__winmm(pDevice);
12561 
12562  return result;
12563 }
12564 
12565 ma_result ma_context_uninit__winmm(ma_context* pContext)
12566 {
12567  ma_assert(pContext != NULL);
12568  ma_assert(pContext->backend == ma_backend_winmm);
12569 
12570  ma_dlclose(pContext, pContext->winmm.hWinMM);
12571  return MA_SUCCESS;
12572 }
12573 
12574 ma_result ma_context_init__winmm(const ma_context_config* pConfig, ma_context* pContext)
12575 {
12576  ma_assert(pContext != NULL);
12577 
12578  (void)pConfig;
12579 
12580  pContext->winmm.hWinMM = ma_dlopen(pContext, "winmm.dll");
12581  if (pContext->winmm.hWinMM == NULL) {
12582  return MA_NO_BACKEND;
12583  }
12584 
12585  pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetNumDevs");
12586  pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetDevCapsA");
12587  pContext->winmm.waveOutOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutOpen");
12588  pContext->winmm.waveOutClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutClose");
12589  pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutPrepareHeader");
12590  pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutUnprepareHeader");
12591  pContext->winmm.waveOutWrite = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutWrite");
12592  pContext->winmm.waveOutReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutReset");
12593  pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetNumDevs");
12594  pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetDevCapsA");
12595  pContext->winmm.waveInOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInOpen");
12596  pContext->winmm.waveInClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInClose");
12597  pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInPrepareHeader");
12598  pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInUnprepareHeader");
12599  pContext->winmm.waveInAddBuffer = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInAddBuffer");
12600  pContext->winmm.waveInStart = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInStart");
12601  pContext->winmm.waveInReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInReset");
12602 
12603  pContext->onUninit = ma_context_uninit__winmm;
12604  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__winmm;
12605  pContext->onEnumDevices = ma_context_enumerate_devices__winmm;
12606  pContext->onGetDeviceInfo = ma_context_get_device_info__winmm;
12607  pContext->onDeviceInit = ma_device_init__winmm;
12608  pContext->onDeviceUninit = ma_device_uninit__winmm;
12609  pContext->onDeviceStart = NULL; /* Not used with synchronous backends. */
12610  pContext->onDeviceStop = NULL; /* Not used with synchronous backends. */
12611  pContext->onDeviceMainLoop = ma_device_main_loop__winmm;
12612 
12613  return MA_SUCCESS;
12614 }
12615 #endif
12616 
12617 
12618 
12619 
12620 /******************************************************************************
12621 
12622 ALSA Backend
12623 
12624 ******************************************************************************/
12625 #ifdef MA_HAS_ALSA
12626 
12627 #ifdef MA_NO_RUNTIME_LINKING
12628 #include <alsa/asoundlib.h>
12629 typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t;
12630 typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t;
12631 typedef snd_pcm_stream_t ma_snd_pcm_stream_t;
12632 typedef snd_pcm_format_t ma_snd_pcm_format_t;
12633 typedef snd_pcm_access_t ma_snd_pcm_access_t;
12634 typedef snd_pcm_t ma_snd_pcm_t;
12635 typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
12636 typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
12637 typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
12638 typedef snd_pcm_info_t ma_snd_pcm_info_t;
12639 typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t;
12640 typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t;
12641 
12642 /* snd_pcm_stream_t */
12643 #define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK
12644 #define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE
12645 
12646 /* snd_pcm_format_t */
12647 #define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN
12648 #define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8
12649 #define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE
12650 #define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE
12651 #define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE
12652 #define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE
12653 #define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE
12654 #define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE
12655 #define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE
12656 #define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE
12657 #define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE
12658 #define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE
12659 #define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW
12660 #define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW
12661 #define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE
12662 #define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE
12663 
12664 /* ma_snd_pcm_access_t */
12665 #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED
12666 #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED
12667 #define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX
12668 #define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED
12669 #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED
12670 
12671 /* Channel positions. */
12672 #define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN
12673 #define MA_SND_CHMAP_NA SND_CHMAP_NA
12674 #define MA_SND_CHMAP_MONO SND_CHMAP_MONO
12675 #define MA_SND_CHMAP_FL SND_CHMAP_FL
12676 #define MA_SND_CHMAP_FR SND_CHMAP_FR
12677 #define MA_SND_CHMAP_RL SND_CHMAP_RL
12678 #define MA_SND_CHMAP_RR SND_CHMAP_RR
12679 #define MA_SND_CHMAP_FC SND_CHMAP_FC
12680 #define MA_SND_CHMAP_LFE SND_CHMAP_LFE
12681 #define MA_SND_CHMAP_SL SND_CHMAP_SL
12682 #define MA_SND_CHMAP_SR SND_CHMAP_SR
12683 #define MA_SND_CHMAP_RC SND_CHMAP_RC
12684 #define MA_SND_CHMAP_FLC SND_CHMAP_FLC
12685 #define MA_SND_CHMAP_FRC SND_CHMAP_FRC
12686 #define MA_SND_CHMAP_RLC SND_CHMAP_RLC
12687 #define MA_SND_CHMAP_RRC SND_CHMAP_RRC
12688 #define MA_SND_CHMAP_FLW SND_CHMAP_FLW
12689 #define MA_SND_CHMAP_FRW SND_CHMAP_FRW
12690 #define MA_SND_CHMAP_FLH SND_CHMAP_FLH
12691 #define MA_SND_CHMAP_FCH SND_CHMAP_FCH
12692 #define MA_SND_CHMAP_FRH SND_CHMAP_FRH
12693 #define MA_SND_CHMAP_TC SND_CHMAP_TC
12694 #define MA_SND_CHMAP_TFL SND_CHMAP_TFL
12695 #define MA_SND_CHMAP_TFR SND_CHMAP_TFR
12696 #define MA_SND_CHMAP_TFC SND_CHMAP_TFC
12697 #define MA_SND_CHMAP_TRL SND_CHMAP_TRL
12698 #define MA_SND_CHMAP_TRR SND_CHMAP_TRR
12699 #define MA_SND_CHMAP_TRC SND_CHMAP_TRC
12700 #define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC
12701 #define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC
12702 #define MA_SND_CHMAP_TSL SND_CHMAP_TSL
12703 #define MA_SND_CHMAP_TSR SND_CHMAP_TSR
12704 #define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE
12705 #define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE
12706 #define MA_SND_CHMAP_BC SND_CHMAP_BC
12707 #define MA_SND_CHMAP_BLC SND_CHMAP_BLC
12708 #define MA_SND_CHMAP_BRC SND_CHMAP_BRC
12709 
12710 /* Open mode flags. */
12711 #define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE
12712 #define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS
12713 #define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT
12714 #else
12715 #include <errno.h> /* For EPIPE, etc. */
12716 typedef unsigned long ma_snd_pcm_uframes_t;
12717 typedef long ma_snd_pcm_sframes_t;
12718 typedef int ma_snd_pcm_stream_t;
12719 typedef int ma_snd_pcm_format_t;
12720 typedef int ma_snd_pcm_access_t;
12721 typedef struct ma_snd_pcm_t ma_snd_pcm_t;
12722 typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
12723 typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
12724 typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
12725 typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t;
12726 typedef struct
12727 {
12728  void* addr;
12729  unsigned int first;
12730  unsigned int step;
12731 } ma_snd_pcm_channel_area_t;
12732 typedef struct
12733 {
12734  unsigned int channels;
12735  unsigned int pos[1];
12736 } ma_snd_pcm_chmap_t;
12737 
12738 /* snd_pcm_state_t */
12739 #define MA_SND_PCM_STATE_OPEN 0
12740 #define MA_SND_PCM_STATE_SETUP 1
12741 #define MA_SND_PCM_STATE_PREPARED 2
12742 #define MA_SND_PCM_STATE_RUNNING 3
12743 #define MA_SND_PCM_STATE_XRUN 4
12744 #define MA_SND_PCM_STATE_DRAINING 5
12745 #define MA_SND_PCM_STATE_PAUSED 6
12746 #define MA_SND_PCM_STATE_SUSPENDED 7
12747 #define MA_SND_PCM_STATE_DISCONNECTED 8
12748 
12749 /* snd_pcm_stream_t */
12750 #define MA_SND_PCM_STREAM_PLAYBACK 0
12751 #define MA_SND_PCM_STREAM_CAPTURE 1
12752 
12753 /* snd_pcm_format_t */
12754 #define MA_SND_PCM_FORMAT_UNKNOWN -1
12755 #define MA_SND_PCM_FORMAT_U8 1
12756 #define MA_SND_PCM_FORMAT_S16_LE 2
12757 #define MA_SND_PCM_FORMAT_S16_BE 3
12758 #define MA_SND_PCM_FORMAT_S24_LE 6
12759 #define MA_SND_PCM_FORMAT_S24_BE 7
12760 #define MA_SND_PCM_FORMAT_S32_LE 10
12761 #define MA_SND_PCM_FORMAT_S32_BE 11
12762 #define MA_SND_PCM_FORMAT_FLOAT_LE 14
12763 #define MA_SND_PCM_FORMAT_FLOAT_BE 15
12764 #define MA_SND_PCM_FORMAT_FLOAT64_LE 16
12765 #define MA_SND_PCM_FORMAT_FLOAT64_BE 17
12766 #define MA_SND_PCM_FORMAT_MU_LAW 20
12767 #define MA_SND_PCM_FORMAT_A_LAW 21
12768 #define MA_SND_PCM_FORMAT_S24_3LE 32
12769 #define MA_SND_PCM_FORMAT_S24_3BE 33
12770 
12771 /* snd_pcm_access_t */
12772 #define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0
12773 #define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1
12774 #define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2
12775 #define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3
12776 #define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4
12777 
12778 /* Channel positions. */
12779 #define MA_SND_CHMAP_UNKNOWN 0
12780 #define MA_SND_CHMAP_NA 1
12781 #define MA_SND_CHMAP_MONO 2
12782 #define MA_SND_CHMAP_FL 3
12783 #define MA_SND_CHMAP_FR 4
12784 #define MA_SND_CHMAP_RL 5
12785 #define MA_SND_CHMAP_RR 6
12786 #define MA_SND_CHMAP_FC 7
12787 #define MA_SND_CHMAP_LFE 8
12788 #define MA_SND_CHMAP_SL 9
12789 #define MA_SND_CHMAP_SR 10
12790 #define MA_SND_CHMAP_RC 11
12791 #define MA_SND_CHMAP_FLC 12
12792 #define MA_SND_CHMAP_FRC 13
12793 #define MA_SND_CHMAP_RLC 14
12794 #define MA_SND_CHMAP_RRC 15
12795 #define MA_SND_CHMAP_FLW 16
12796 #define MA_SND_CHMAP_FRW 17
12797 #define MA_SND_CHMAP_FLH 18
12798 #define MA_SND_CHMAP_FCH 19
12799 #define MA_SND_CHMAP_FRH 20
12800 #define MA_SND_CHMAP_TC 21
12801 #define MA_SND_CHMAP_TFL 22
12802 #define MA_SND_CHMAP_TFR 23
12803 #define MA_SND_CHMAP_TFC 24
12804 #define MA_SND_CHMAP_TRL 25
12805 #define MA_SND_CHMAP_TRR 26
12806 #define MA_SND_CHMAP_TRC 27
12807 #define MA_SND_CHMAP_TFLC 28
12808 #define MA_SND_CHMAP_TFRC 29
12809 #define MA_SND_CHMAP_TSL 30
12810 #define MA_SND_CHMAP_TSR 31
12811 #define MA_SND_CHMAP_LLFE 32
12812 #define MA_SND_CHMAP_RLFE 33
12813 #define MA_SND_CHMAP_BC 34
12814 #define MA_SND_CHMAP_BLC 35
12815 #define MA_SND_CHMAP_BRC 36
12816 
12817 /* Open mode flags. */
12818 #define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000
12819 #define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000
12820 #define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000
12821 #endif
12822 
12823 typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode);
12824 typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm);
12825 typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void);
12826 typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
12827 typedef int (* ma_snd_pcm_hw_params_set_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val);
12828 typedef int (* ma_snd_pcm_hw_params_set_format_first_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);
12829 typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask);
12830 typedef int (* ma_snd_pcm_hw_params_set_channels_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val);
12831 typedef int (* ma_snd_pcm_hw_params_set_rate_resample_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);
12832 typedef int (* ma_snd_pcm_hw_params_set_rate_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
12833 typedef int (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val);
12834 typedef int (* ma_snd_pcm_hw_params_set_periods_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
12835 typedef int (* ma_snd_pcm_hw_params_set_access_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access);
12836 typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);
12837 typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
12838 typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
12839 typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
12840 typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
12841 typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
12842 typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
12843 typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val);
12844 typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
12845 typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access);
12846 typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
12847 typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void);
12848 typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
12849 typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val);
12850 typedef int (* ma_snd_pcm_sw_params_set_avail_min_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val);
12851 typedef int (* ma_snd_pcm_sw_params_set_start_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val);
12852 typedef int (* ma_snd_pcm_sw_params_set_stop_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val);
12853 typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
12854 typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void);
12855 typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val);
12856 typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm);
12857 typedef int (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm);
12858 typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm);
12859 typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm);
12860 typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm);
12861 typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm);
12862 typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints);
12863 typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id);
12864 typedef int (* ma_snd_card_get_index_proc) (const char *name);
12865 typedef int (* ma_snd_device_name_free_hint_proc) (void **hints);
12866 typedef int (* ma_snd_pcm_mmap_begin_proc) (ma_snd_pcm_t *pcm, const ma_snd_pcm_channel_area_t **areas, ma_snd_pcm_uframes_t *offset, ma_snd_pcm_uframes_t *frames);
12867 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_mmap_commit_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_uframes_t offset, ma_snd_pcm_uframes_t frames);
12868 typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent);
12869 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size);
12870 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size);
12871 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm);
12872 typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm);
12873 typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout);
12874 typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info);
12875 typedef size_t (* ma_snd_pcm_info_sizeof_proc) ();
12876 typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info);
12877 typedef int (* ma_snd_config_update_free_global_proc) ();
12878 
12879 /* This array specifies each of the common devices that can be used for both playback and capture. */
12880 const char* g_maCommonDeviceNamesALSA[] = {
12881  "default",
12882  "null",
12883  "pulse",
12884  "jack"
12885 };
12886 
12887 /* This array allows us to blacklist specific playback devices. */
12888 const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = {
12889  ""
12890 };
12891 
12892 /* This array allows us to blacklist specific capture devices. */
12893 const char* g_maBlacklistedCaptureDeviceNamesALSA[] = {
12894  ""
12895 };
12896 
12897 
12898 /*
12899 This array allows miniaudio to control device-specific default buffer sizes. This uses a scaling factor. Order is important. If
12900 any part of the string is present in the device's name, the associated scale will be used.
12901 */
12902 static struct
12903 {
12904  const char* name;
12905  float scale;
12906 } g_maDefaultBufferSizeScalesALSA[] = {
12907  {"bcm2835 IEC958/HDMI", 2.0f},
12908  {"bcm2835 ALSA", 2.0f}
12909 };
12910 
12911 float ma_find_default_buffer_size_scale__alsa(const char* deviceName)
12912 {
12913  size_t i;
12914 
12915  if (deviceName == NULL) {
12916  return 1;
12917  }
12918 
12919  for (i = 0; i < ma_countof(g_maDefaultBufferSizeScalesALSA); ++i) {
12920  if (strstr(g_maDefaultBufferSizeScalesALSA[i].name, deviceName) != NULL) {
12921  return g_maDefaultBufferSizeScalesALSA[i].scale;
12922  }
12923  }
12924 
12925  return 1;
12926 }
12927 
12928 ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format)
12929 {
12930  ma_snd_pcm_format_t ALSAFormats[] = {
12931  MA_SND_PCM_FORMAT_UNKNOWN, /* ma_format_unknown */
12932  MA_SND_PCM_FORMAT_U8, /* ma_format_u8 */
12933  MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */
12934  MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */
12935  MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */
12936  MA_SND_PCM_FORMAT_FLOAT_LE /* ma_format_f32 */
12937  };
12938 
12939  if (ma_is_big_endian()) {
12940  ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN;
12941  ALSAFormats[1] = MA_SND_PCM_FORMAT_U8;
12942  ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE;
12943  ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE;
12944  ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE;
12945  ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE;
12946  }
12947 
12948  return ALSAFormats[format];
12949 }
12950 
12951 ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA)
12952 {
12953  if (ma_is_little_endian()) {
12954  switch (formatALSA) {
12955  case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16;
12956  case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24;
12957  case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32;
12958  case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32;
12959  default: break;
12960  }
12961  } else {
12962  switch (formatALSA) {
12963  case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16;
12964  case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24;
12965  case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32;
12966  case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32;
12967  default: break;
12968  }
12969  }
12970 
12971  /* Endian agnostic. */
12972  switch (formatALSA) {
12973  case MA_SND_PCM_FORMAT_U8: return ma_format_u8;
12974  default: return ma_format_unknown;
12975  }
12976 }
12977 
12978 ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos)
12979 {
12980  switch (alsaChannelPos)
12981  {
12982  case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO;
12983  case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT;
12984  case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT;
12985  case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT;
12986  case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT;
12987  case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER;
12988  case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE;
12989  case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT;
12990  case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT;
12991  case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER;
12992  case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER;
12993  case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER;
12994  case MA_SND_CHMAP_RLC: return 0;
12995  case MA_SND_CHMAP_RRC: return 0;
12996  case MA_SND_CHMAP_FLW: return 0;
12997  case MA_SND_CHMAP_FRW: return 0;
12998  case MA_SND_CHMAP_FLH: return 0;
12999  case MA_SND_CHMAP_FCH: return 0;
13000  case MA_SND_CHMAP_FRH: return 0;
13001  case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER;
13002  case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT;
13003  case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT;
13004  case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER;
13005  case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT;
13006  case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT;
13007  case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER;
13008  default: break;
13009  }
13010 
13011  return 0;
13012 }
13013 
13014 ma_bool32 ma_is_common_device_name__alsa(const char* name)
13015 {
13016  size_t iName;
13017  for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) {
13018  if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) {
13019  return MA_TRUE;
13020  }
13021  }
13022 
13023  return MA_FALSE;
13024 }
13025 
13026 
13027 ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name)
13028 {
13029  size_t iName;
13030  for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) {
13031  if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) {
13032  return MA_TRUE;
13033  }
13034  }
13035 
13036  return MA_FALSE;
13037 }
13038 
13039 ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name)
13040 {
13041  size_t iName;
13042  for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) {
13043  if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) {
13044  return MA_TRUE;
13045  }
13046  }
13047 
13048  return MA_FALSE;
13049 }
13050 
13051 ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name)
13052 {
13053  if (deviceType == ma_device_type_playback) {
13054  return ma_is_playback_device_blacklisted__alsa(name);
13055  } else {
13056  return ma_is_capture_device_blacklisted__alsa(name);
13057  }
13058 }
13059 
13060 
13061 const char* ma_find_char(const char* str, char c, int* index)
13062 {
13063  int i = 0;
13064  for (;;) {
13065  if (str[i] == '\0') {
13066  if (index) *index = -1;
13067  return NULL;
13068  }
13069 
13070  if (str[i] == c) {
13071  if (index) *index = i;
13072  return str + i;
13073  }
13074 
13075  i += 1;
13076  }
13077 
13078  /* Should never get here, but treat it as though the character was not found to make me feel better inside. */
13079  if (index) *index = -1;
13080  return NULL;
13081 }
13082 
13083 ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid)
13084 {
13085  /* This function is just checking whether or not hwid is in "hw:%d,%d" format. */
13086 
13087  int commaPos;
13088  const char* dev;
13089  int i;
13090 
13091  if (hwid == NULL) {
13092  return MA_FALSE;
13093  }
13094 
13095  if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') {
13096  return MA_FALSE;
13097  }
13098 
13099  hwid += 3;
13100 
13101  dev = ma_find_char(hwid, ',', &commaPos);
13102  if (dev == NULL) {
13103  return MA_FALSE;
13104  } else {
13105  dev += 1; /* Skip past the ",". */
13106  }
13107 
13108  /* Check if the part between the ":" and the "," contains only numbers. If not, return false. */
13109  for (i = 0; i < commaPos; ++i) {
13110  if (hwid[i] < '0' || hwid[i] > '9') {
13111  return MA_FALSE;
13112  }
13113  }
13114 
13115  /* Check if everything after the "," is numeric. If not, return false. */
13116  i = 0;
13117  while (dev[i] != '\0') {
13118  if (dev[i] < '0' || dev[i] > '9') {
13119  return MA_FALSE;
13120  }
13121  i += 1;
13122  }
13123 
13124  return MA_TRUE;
13125 }
13126 
13127 int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* dst, size_t dstSize, const char* src) /* Returns 0 on success, non-0 on error. */
13128 {
13129  /* src should look something like this: "hw:CARD=I82801AAICH,DEV=0" */
13130 
13131  int colonPos;
13132  int commaPos;
13133  char card[256];
13134  const char* dev;
13135  int cardIndex;
13136 
13137  if (dst == NULL) {
13138  return -1;
13139  }
13140  if (dstSize < 7) {
13141  return -1; /* Absolute minimum size of the output buffer is 7 bytes. */
13142  }
13143 
13144  *dst = '\0'; /* Safety. */
13145  if (src == NULL) {
13146  return -1;
13147  }
13148 
13149  /* If the input name is already in "hw:%d,%d" format, just return that verbatim. */
13150  if (ma_is_device_name_in_hw_format__alsa(src)) {
13151  return ma_strcpy_s(dst, dstSize, src);
13152  }
13153 
13154  src = ma_find_char(src, ':', &colonPos);
13155  if (src == NULL) {
13156  return -1; /* Couldn't find a colon */
13157  }
13158 
13159  dev = ma_find_char(src, ',', &commaPos);
13160  if (dev == NULL) {
13161  dev = "0";
13162  ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1); /* +6 = ":CARD=" */
13163  } else {
13164  dev = dev + 5; /* +5 = ",DEV=" */
13165  ma_strncpy_s(card, sizeof(card), src+6, commaPos-6); /* +6 = ":CARD=" */
13166  }
13167 
13168  cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card);
13169  if (cardIndex < 0) {
13170  return -2; /* Failed to retrieve the card index. */
13171  }
13172 
13173  /*printf("TESTING: CARD=%s,DEV=%s\n", card, dev); */
13174 
13175 
13176  /* Construction. */
13177  dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':';
13178  if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) {
13179  return -3;
13180  }
13181  if (ma_strcat_s(dst, dstSize, ",") != 0) {
13182  return -3;
13183  }
13184  if (ma_strcat_s(dst, dstSize, dev) != 0) {
13185  return -3;
13186  }
13187 
13188  return 0;
13189 }
13190 
13191 ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID)
13192 {
13193  ma_uint32 i;
13194 
13195  ma_assert(pHWID != NULL);
13196 
13197  for (i = 0; i < count; ++i) {
13198  if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) {
13199  return MA_TRUE;
13200  }
13201  }
13202 
13203  return MA_FALSE;
13204 }
13205 
13206 
13207 ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_snd_pcm_t** ppPCM)
13208 {
13209  ma_snd_pcm_t* pPCM;
13210  ma_snd_pcm_stream_t stream;
13211  int openMode;
13212 
13213  ma_assert(pContext != NULL);
13214  ma_assert(ppPCM != NULL);
13215 
13216  *ppPCM = NULL;
13217  pPCM = NULL;
13218 
13219  stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE;
13220  openMode = MA_SND_PCM_NO_AUTO_RESAMPLE | MA_SND_PCM_NO_AUTO_CHANNELS | MA_SND_PCM_NO_AUTO_FORMAT;
13221 
13222  if (pDeviceID == NULL) {
13223  ma_bool32 isDeviceOpen;
13224  size_t i;
13225 
13226  /*
13227  We're opening the default device. I don't know if trying anything other than "default" is necessary, but it makes
13228  me feel better to try as hard as we can get to get _something_ working.
13229  */
13230  const char* defaultDeviceNames[] = {
13231  "default",
13232  NULL,
13233  NULL,
13234  NULL,
13235  NULL,
13236  NULL,
13237  NULL
13238  };
13239 
13240  if (shareMode == ma_share_mode_exclusive) {
13241  defaultDeviceNames[1] = "hw";
13242  defaultDeviceNames[2] = "hw:0";
13243  defaultDeviceNames[3] = "hw:0,0";
13244  } else {
13245  if (deviceType == ma_device_type_playback) {
13246  defaultDeviceNames[1] = "dmix";
13247  defaultDeviceNames[2] = "dmix:0";
13248  defaultDeviceNames[3] = "dmix:0,0";
13249  } else {
13250  defaultDeviceNames[1] = "dsnoop";
13251  defaultDeviceNames[2] = "dsnoop:0";
13252  defaultDeviceNames[3] = "dsnoop:0,0";
13253  }
13254  defaultDeviceNames[4] = "hw";
13255  defaultDeviceNames[5] = "hw:0";
13256  defaultDeviceNames[6] = "hw:0,0";
13257  }
13258 
13259  isDeviceOpen = MA_FALSE;
13260  for (i = 0; i < ma_countof(defaultDeviceNames); ++i) {
13261  if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') {
13262  if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) {
13263  isDeviceOpen = MA_TRUE;
13264  break;
13265  }
13266  }
13267  }
13268 
13269  if (!isDeviceOpen) {
13270  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
13271  }
13272  } else {
13273  /*
13274  We're trying to open a specific device. There's a few things to consider here:
13275 
13276  miniaudio recongnizes a special format of device id that excludes the "hw", "dmix", etc. prefix. It looks like this: ":0,0", ":0,1", etc. When
13277  an ID of this format is specified, it indicates to miniaudio that it can try different combinations of plugins ("hw", "dmix", etc.) until it
13278  finds an appropriate one that works. This comes in very handy when trying to open a device in shared mode ("dmix"), vs exclusive mode ("hw").
13279  */
13280 
13281  /* May end up needing to make small adjustments to the ID, so make a copy. */
13282  ma_device_id deviceID = *pDeviceID;
13283  ma_bool32 isDeviceOpen = MA_FALSE;
13284 
13285  if (deviceID.alsa[0] != ':') {
13286  /* The ID is not in ":0,0" format. Use the ID exactly as-is. */
13287  if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode) == 0) {
13288  isDeviceOpen = MA_TRUE;
13289  }
13290  } else {
13291  char hwid[256];
13292 
13293  /* The ID is in ":0,0" format. Try different plugins depending on the shared mode. */
13294  if (deviceID.alsa[1] == '\0') {
13295  deviceID.alsa[0] = '\0'; /* An ID of ":" should be converted to "". */
13296  }
13297 
13298  if (shareMode == ma_share_mode_shared) {
13299  if (deviceType == ma_device_type_playback) {
13300  ma_strcpy_s(hwid, sizeof(hwid), "dmix");
13301  } else {
13302  ma_strcpy_s(hwid, sizeof(hwid), "dsnoop");
13303  }
13304 
13305  if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
13306  if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode) == 0) {
13307  isDeviceOpen = MA_TRUE;
13308  }
13309  }
13310  }
13311 
13312  /* If at this point we still don't have an open device it means we're either preferencing exclusive mode or opening with "dmix"/"dsnoop" failed. */
13313  if (!isDeviceOpen) {
13314  ma_strcpy_s(hwid, sizeof(hwid), "hw");
13315  if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
13316  if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode) == 0) {
13317  isDeviceOpen = MA_TRUE;
13318  }
13319  }
13320  }
13321  }
13322 
13323  if (!isDeviceOpen) {
13324  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
13325  }
13326  }
13327 
13328  *ppPCM = pPCM;
13329  return MA_SUCCESS;
13330 }
13331 
13332 
13333 ma_bool32 ma_context_is_device_id_equal__alsa(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
13334 {
13335  ma_assert(pContext != NULL);
13336  ma_assert(pID0 != NULL);
13337  ma_assert(pID1 != NULL);
13338  (void)pContext;
13339 
13340  return ma_strcmp(pID0->alsa, pID1->alsa) == 0;
13341 }
13342 
13343 ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
13344 {
13345  ma_bool32 cbResult = MA_TRUE;
13346  char** ppDeviceHints;
13347  ma_device_id* pUniqueIDs = NULL;
13348  ma_uint32 uniqueIDCount = 0;
13349  char** ppNextDeviceHint;
13350 
13351  ma_assert(pContext != NULL);
13352  ma_assert(callback != NULL);
13353 
13354  ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock);
13355 
13356  if (((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints) < 0) {
13357  ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
13358  return MA_NO_BACKEND;
13359  }
13360 
13361  ppNextDeviceHint = ppDeviceHints;
13362  while (*ppNextDeviceHint != NULL) {
13363  char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
13364  char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
13365  char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
13367  ma_bool32 stopEnumeration = MA_FALSE;
13368  char hwid[sizeof(pUniqueIDs->alsa)];
13369  ma_device_info deviceInfo;
13370 
13371  if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) {
13372  deviceType = ma_device_type_playback;
13373  }
13374  if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) {
13375  deviceType = ma_device_type_capture;
13376  }
13377 
13378  if (NAME != NULL) {
13379  if (pContext->alsa.useVerboseDeviceEnumeration) {
13380  /* Verbose mode. Use the name exactly as-is. */
13381  ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
13382  } else {
13383  /* Simplified mode. Use ":%d,%d" format. */
13384  if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) {
13385  /*
13386  At this point, hwid looks like "hw:0,0". In simplified enumeration mode, we actually want to strip off the
13387  plugin name so it looks like ":0,0". The reason for this is that this special format is detected at device
13388  initialization time and is used as an indicator to try and use the most appropriate plugin depending on the
13389  device type and sharing mode.
13390  */
13391  char* dst = hwid;
13392  char* src = hwid+2;
13393  while ((*dst++ = *src++));
13394  } else {
13395  /* Conversion to "hw:%d,%d" failed. Just use the name as-is. */
13396  ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
13397  }
13398 
13399  if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) {
13400  goto next_device; /* The device has already been enumerated. Move on to the next one. */
13401  } else {
13402  /* The device has not yet been enumerated. Make sure it's added to our list so that it's not enumerated again. */
13403  ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, sizeof(*pUniqueIDs) * (uniqueIDCount + 1));
13404  if (pNewUniqueIDs == NULL) {
13405  goto next_device; /* Failed to allocate memory. */
13406  }
13407 
13408  pUniqueIDs = pNewUniqueIDs;
13409  ma_copy_memory(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid));
13410  uniqueIDCount += 1;
13411  }
13412  }
13413  } else {
13414  ma_zero_memory(hwid, sizeof(hwid));
13415  }
13416 
13417  ma_zero_object(&deviceInfo);
13418  ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1);
13419 
13420  /*
13421  DESC is the friendly name. We treat this slightly differently depending on whether or not we are using verbose
13422  device enumeration. In verbose mode we want to take the entire description so that the end-user can distinguish
13423  between the subdevices of each card/dev pair. In simplified mode, however, we only want the first part of the
13424  description.
13425 
13426  The value in DESC seems to be split into two lines, with the first line being the name of the device and the
13427  second line being a description of the device. I don't like having the description be across two lines because
13428  it makes formatting ugly and annoying. I'm therefore deciding to put it all on a single line with the second line
13429  being put into parentheses. In simplified mode I'm just stripping the second line entirely.
13430  */
13431  if (DESC != NULL) {
13432  int lfPos;
13433  const char* line2 = ma_find_char(DESC, '\n', &lfPos);
13434  if (line2 != NULL) {
13435  line2 += 1; /* Skip past the new-line character. */
13436 
13437  if (pContext->alsa.useVerboseDeviceEnumeration) {
13438  /* Verbose mode. Put the second line in brackets. */
13439  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
13440  ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " (");
13441  ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2);
13442  ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")");
13443  } else {
13444  /* Simplified mode. Strip the second line entirely. */
13445  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
13446  }
13447  } else {
13448  /* There's no second line. Just copy the whole description. */
13449  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1);
13450  }
13451  }
13452 
13453  if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) {
13454  cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
13455  }
13456 
13457  /*
13458  Some devices are both playback and capture, but they are only enumerated by ALSA once. We need to fire the callback
13459  again for the other device type in this case. We do this for known devices.
13460  */
13461  if (cbResult) {
13462  if (ma_is_common_device_name__alsa(NAME)) {
13463  if (deviceType == ma_device_type_playback) {
13464  if (!ma_is_capture_device_blacklisted__alsa(NAME)) {
13465  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
13466  }
13467  } else {
13468  if (!ma_is_playback_device_blacklisted__alsa(NAME)) {
13469  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
13470  }
13471  }
13472  }
13473  }
13474 
13475  if (cbResult == MA_FALSE) {
13476  stopEnumeration = MA_TRUE;
13477  }
13478 
13479  next_device:
13480  free(NAME);
13481  free(DESC);
13482  free(IOID);
13483  ppNextDeviceHint += 1;
13484 
13485  /* We need to stop enumeration if the callback returned false. */
13486  if (stopEnumeration) {
13487  break;
13488  }
13489  }
13490 
13491  ma_free(pUniqueIDs);
13492  ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
13493 
13494  ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
13495 
13496  return MA_SUCCESS;
13497 }
13498 
13499 
13500 typedef struct
13501 {
13502  ma_device_type deviceType;
13503  const ma_device_id* pDeviceID;
13504  ma_share_mode shareMode;
13505  ma_device_info* pDeviceInfo;
13506  ma_bool32 foundDevice;
13507 } ma_context_get_device_info_enum_callback_data__alsa;
13508 
13509 ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData)
13510 {
13511  ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData;
13512  ma_assert(pData != NULL);
13513 
13514  if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
13515  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
13516  pData->foundDevice = MA_TRUE;
13517  } else {
13518  if (pData->deviceType == deviceType && ma_context_is_device_id_equal__alsa(pContext, pData->pDeviceID, &pDeviceInfo->id)) {
13519  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
13520  pData->foundDevice = MA_TRUE;
13521  }
13522  }
13523 
13524  /* Keep enumerating until we have found the device. */
13525  return !pData->foundDevice;
13526 }
13527 
13528 ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
13529 {
13530  ma_context_get_device_info_enum_callback_data__alsa data;
13531  ma_result result;
13532  ma_snd_pcm_t* pPCM;
13533  ma_snd_pcm_hw_params_t* pHWParams;
13534  ma_snd_pcm_format_mask_t* pFormatMask;
13535  int sampleRateDir = 0;
13536 
13537  ma_assert(pContext != NULL);
13538 
13539  /* We just enumerate to find basic information about the device. */
13540  data.deviceType = deviceType;
13541  data.pDeviceID = pDeviceID;
13542  data.shareMode = shareMode;
13543  data.pDeviceInfo = pDeviceInfo;
13544  data.foundDevice = MA_FALSE;
13545  result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data);
13546  if (result != MA_SUCCESS) {
13547  return result;
13548  }
13549 
13550  if (!data.foundDevice) {
13551  return MA_NO_DEVICE;
13552  }
13553 
13554  /* For detailed info we need to open the device. */
13555  result = ma_context_open_pcm__alsa(pContext, shareMode, deviceType, pDeviceID, &pPCM);
13556  if (result != MA_SUCCESS) {
13557  return result;
13558  }
13559 
13560  /* We need to initialize a HW parameters object in order to know what formats are supported. */
13561  pHWParams = (ma_snd_pcm_hw_params_t*)calloc(1, ((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)());
13562  if (pHWParams == NULL) {
13563  return MA_OUT_OF_MEMORY;
13564  }
13565 
13566  if (((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams) < 0) {
13567  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
13568  }
13569 
13570  ((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &pDeviceInfo->minChannels);
13571  ((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &pDeviceInfo->maxChannels);
13572  ((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &pDeviceInfo->minSampleRate, &sampleRateDir);
13573  ((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &pDeviceInfo->maxSampleRate, &sampleRateDir);
13574 
13575  /* Formats. */
13576  pFormatMask = (ma_snd_pcm_format_mask_t*)calloc(1, ((ma_snd_pcm_format_mask_sizeof_proc)pContext->alsa.snd_pcm_format_mask_sizeof)());
13577  if (pFormatMask == NULL) {
13578  return MA_OUT_OF_MEMORY;
13579  }
13580 
13581  ((ma_snd_pcm_hw_params_get_format_mask_proc)pContext->alsa.snd_pcm_hw_params_get_format_mask)(pHWParams, pFormatMask);
13582 
13583  pDeviceInfo->formatCount = 0;
13584  if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_U8)) {
13585  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_u8;
13586  }
13587  if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_S16_LE)) {
13588  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s16;
13589  }
13590  if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_S24_3LE)) {
13591  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s24;
13592  }
13593  if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_S32_LE)) {
13594  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s32;
13595  }
13596  if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, MA_SND_PCM_FORMAT_FLOAT_LE)) {
13597  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_f32;
13598  }
13599 
13600  ma_free(pFormatMask);
13601  ma_free(pHWParams);
13602 
13603  ((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
13604  return MA_SUCCESS;
13605 }
13606 
13607 
13608 #if 0
13609 /*
13610 Waits for a number of frames to become available for either capture or playback. The return
13611 value is the number of frames available.
13612 
13613 This will return early if the main loop is broken with ma_device__break_main_loop().
13614 */
13615 ma_uint32 ma_device__wait_for_frames__alsa(ma_device* pDevice, ma_bool32* pRequiresRestart)
13616 {
13617  ma_assert(pDevice != NULL);
13618 
13619  if (pRequiresRestart) *pRequiresRestart = MA_FALSE;
13620 
13621  /* I want it so that this function returns the period size in frames. We just wait until that number of frames are available and then return. */
13622  ma_uint32 periodSizeInFrames = pDevice->bufferSizeInFrames / pDevice->periods;
13623  while (!pDevice->alsa.breakFromMainLoop) {
13624  ma_snd_pcm_sframes_t framesAvailable = ((ma_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((ma_snd_pcm_t*)pDevice->alsa.pPCM);
13625  if (framesAvailable < 0) {
13626  if (framesAvailable == -EPIPE) {
13627  if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, framesAvailable, MA_TRUE) < 0) {
13628  return 0;
13629  }
13630 
13631  /* A device recovery means a restart for mmap mode. */
13632  if (pRequiresRestart) {
13633  *pRequiresRestart = MA_TRUE;
13634  }
13635 
13636  /* Try again, but if it fails this time just return an error. */
13637  framesAvailable = ((ma_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((ma_snd_pcm_t*)pDevice->alsa.pPCM);
13638  if (framesAvailable < 0) {
13639  return 0;
13640  }
13641  }
13642  }
13643 
13644  if (framesAvailable >= periodSizeInFrames) {
13645  return periodSizeInFrames;
13646  }
13647 
13648  if (framesAvailable < periodSizeInFrames) {
13649  /* Less than a whole period is available so keep waiting. */
13650  int waitResult = ((ma_snd_pcm_wait_proc)pDevice->pContext->alsa.snd_pcm_wait)((ma_snd_pcm_t*)pDevice->alsa.pPCM, -1);
13651  if (waitResult < 0) {
13652  if (waitResult == -EPIPE) {
13653  if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, waitResult, MA_TRUE) < 0) {
13654  return 0;
13655  }
13656 
13657  /* A device recovery means a restart for mmap mode. */
13658  if (pRequiresRestart) {
13659  *pRequiresRestart = MA_TRUE;
13660  }
13661  }
13662  }
13663  }
13664  }
13665 
13666  /* We'll get here if the loop was terminated. Just return whatever's available. */
13667  ma_snd_pcm_sframes_t framesAvailable = ((ma_snd_pcm_avail_update_proc)pDevice->pContext->alsa.snd_pcm_avail_update)((ma_snd_pcm_t*)pDevice->alsa.pPCM);
13668  if (framesAvailable < 0) {
13669  return 0;
13670  }
13671 
13672  return framesAvailable;
13673 }
13674 
13675 ma_bool32 ma_device_read_from_client_and_write__alsa(ma_device* pDevice)
13676 {
13677  ma_assert(pDevice != NULL);
13678  if (!ma_device_is_started(pDevice) && ma_device__get_state(pDevice) != MA_STATE_STARTING) {
13679  return MA_FALSE;
13680  }
13681  if (pDevice->alsa.breakFromMainLoop) {
13682  return MA_FALSE;
13683  }
13684 
13685  if (pDevice->alsa.isUsingMMap) {
13686  /* mmap. */
13687  ma_bool32 requiresRestart;
13688  ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, &requiresRestart);
13689  if (framesAvailable == 0) {
13690  return MA_FALSE;
13691  }
13692 
13693  /* Don't bother asking the client for more audio data if we're just stopping the device anyway. */
13694  if (pDevice->alsa.breakFromMainLoop) {
13695  return MA_FALSE;
13696  }
13697 
13698  const ma_snd_pcm_channel_area_t* pAreas;
13699  ma_snd_pcm_uframes_t mappedOffset;
13700  ma_snd_pcm_uframes_t mappedFrames = framesAvailable;
13701  while (framesAvailable > 0) {
13702  int result = ((ma_snd_pcm_mmap_begin_proc)pDevice->pContext->alsa.snd_pcm_mmap_begin)((ma_snd_pcm_t*)pDevice->alsa.pPCM, &pAreas, &mappedOffset, &mappedFrames);
13703  if (result < 0) {
13704  return MA_FALSE;
13705  }
13706 
13707  if (mappedFrames > 0) {
13708  void* pBuffer = (ma_uint8*)pAreas[0].addr + ((pAreas[0].first + (mappedOffset * pAreas[0].step)) / 8);
13709  ma_device__read_frames_from_client(pDevice, mappedFrames, pBuffer);
13710  }
13711 
13712  result = ((ma_snd_pcm_mmap_commit_proc)pDevice->pContext->alsa.snd_pcm_mmap_commit)((ma_snd_pcm_t*)pDevice->alsa.pPCM, mappedOffset, mappedFrames);
13713  if (result < 0 || (ma_snd_pcm_uframes_t)result != mappedFrames) {
13714  ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, result, MA_TRUE);
13715  return MA_FALSE;
13716  }
13717 
13718  if (requiresRestart) {
13719  if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
13720  return MA_FALSE;
13721  }
13722  }
13723 
13724  if (framesAvailable >= mappedFrames) {
13725  framesAvailable -= mappedFrames;
13726  } else {
13727  framesAvailable = 0;
13728  }
13729  }
13730  } else {
13731  /* readi/writei. */
13732  while (!pDevice->alsa.breakFromMainLoop) {
13733  ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, NULL);
13734  if (framesAvailable == 0) {
13735  continue;
13736  }
13737 
13738  /* Don't bother asking the client for more audio data if we're just stopping the device anyway. */
13739  if (pDevice->alsa.breakFromMainLoop) {
13740  return MA_FALSE;
13741  }
13742 
13743  ma_device__read_frames_from_client(pDevice, framesAvailable, pDevice->alsa.pIntermediaryBuffer);
13744 
13745  ma_snd_pcm_sframes_t framesWritten = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
13746  if (framesWritten < 0) {
13747  if (framesWritten == -EAGAIN) {
13748  continue; /* Just keep trying... */
13749  } else if (framesWritten == -EPIPE) {
13750  /* Underrun. Just recover and try writing again. */
13751  if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, framesWritten, MA_TRUE) < 0) {
13752  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
13753  return MA_FALSE;
13754  }
13755 
13756  framesWritten = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
13757  if (framesWritten < 0) {
13758  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to write data to the internal device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
13759  return MA_FALSE;
13760  }
13761 
13762  break; /* Success. */
13763  } else {
13764  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_writei() failed when writing initial data.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
13765  return MA_FALSE;
13766  }
13767  } else {
13768  break; /* Success. */
13769  }
13770  }
13771  }
13772 
13773  return MA_TRUE;
13774 }
13775 
13776 ma_bool32 ma_device_read_and_send_to_client__alsa(ma_device* pDevice)
13777 {
13778  ma_assert(pDevice != NULL);
13779  if (!ma_device_is_started(pDevice)) {
13780  return MA_FALSE;
13781  }
13782  if (pDevice->alsa.breakFromMainLoop) {
13783  return MA_FALSE;
13784  }
13785 
13786  ma_uint32 framesToSend = 0;
13787  void* pBuffer = NULL;
13788  if (pDevice->alsa.pIntermediaryBuffer == NULL) {
13789  /* mmap. */
13790  ma_bool32 requiresRestart;
13791  ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, &requiresRestart);
13792  if (framesAvailable == 0) {
13793  return MA_FALSE;
13794  }
13795 
13796  const ma_snd_pcm_channel_area_t* pAreas;
13797  ma_snd_pcm_uframes_t mappedOffset;
13798  ma_snd_pcm_uframes_t mappedFrames = framesAvailable;
13799  while (framesAvailable > 0) {
13800  int result = ((ma_snd_pcm_mmap_begin_proc)pDevice->pContext->alsa.snd_pcm_mmap_begin)((ma_snd_pcm_t*)pDevice->alsa.pPCM, &pAreas, &mappedOffset, &mappedFrames);
13801  if (result < 0) {
13802  return MA_FALSE;
13803  }
13804 
13805  if (mappedFrames > 0) {
13806  void* pBuffer = (ma_uint8*)pAreas[0].addr + ((pAreas[0].first + (mappedOffset * pAreas[0].step)) / 8);
13807  ma_device__send_frames_to_client(pDevice, mappedFrames, pBuffer);
13808  }
13809 
13810  result = ((ma_snd_pcm_mmap_commit_proc)pDevice->pContext->alsa.snd_pcm_mmap_commit)((ma_snd_pcm_t*)pDevice->alsa.pPCM, mappedOffset, mappedFrames);
13811  if (result < 0 || (ma_snd_pcm_uframes_t)result != mappedFrames) {
13812  ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, result, MA_TRUE);
13813  return MA_FALSE;
13814  }
13815 
13816  if (requiresRestart) {
13817  if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
13818  return MA_FALSE;
13819  }
13820  }
13821 
13822  if (framesAvailable >= mappedFrames) {
13823  framesAvailable -= mappedFrames;
13824  } else {
13825  framesAvailable = 0;
13826  }
13827  }
13828  } else {
13829  /* readi/writei. */
13830  ma_snd_pcm_sframes_t framesRead = 0;
13831  while (!pDevice->alsa.breakFromMainLoop) {
13832  ma_uint32 framesAvailable = ma_device__wait_for_frames__alsa(pDevice, NULL);
13833  if (framesAvailable == 0) {
13834  continue;
13835  }
13836 
13837  framesRead = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
13838  if (framesRead < 0) {
13839  if (framesRead == -EAGAIN) {
13840  continue; /* Just keep trying... */
13841  } else if (framesRead == -EPIPE) {
13842  /* Overrun. Just recover and try reading again. */
13843  if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCM, framesRead, MA_TRUE) < 0) {
13844  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
13845  return MA_FALSE;
13846  }
13847 
13848  framesRead = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCM, pDevice->alsa.pIntermediaryBuffer, framesAvailable);
13849  if (framesRead < 0) {
13850  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to read data from the internal device.", MA_FAILED_TO_READ_DATA_FROM_DEVICE);
13851  return MA_FALSE;
13852  }
13853 
13854  break; /* Success. */
13855  } else {
13856  return MA_FALSE;
13857  }
13858  } else {
13859  break; /* Success. */
13860  }
13861  }
13862 
13863  framesToSend = framesRead;
13864  pBuffer = pDevice->alsa.pIntermediaryBuffer;
13865  }
13866 
13867  if (framesToSend > 0) {
13868  ma_device__send_frames_to_client(pDevice, framesToSend, pBuffer);
13869  }
13870 
13871  return MA_TRUE;
13872 }
13873 #endif /* 0 */
13874 
13875 void ma_device_uninit__alsa(ma_device* pDevice)
13876 {
13877  ma_assert(pDevice != NULL);
13878 
13879  if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) {
13880  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
13881  }
13882 
13883  if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) {
13884  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
13885  }
13886 }
13887 
13888 ma_result ma_device_init_by_type__alsa(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
13889 {
13890  ma_result result;
13891  ma_snd_pcm_t* pPCM;
13892  ma_bool32 isUsingMMap;
13893  ma_snd_pcm_format_t formatALSA;
13894  ma_share_mode shareMode;
13895  ma_device_id* pDeviceID;
13896  ma_format internalFormat;
13897  ma_uint32 internalChannels;
13898  ma_uint32 internalSampleRate;
13899  ma_channel internalChannelMap[MA_MAX_CHANNELS];
13900  ma_uint32 internalBufferSizeInFrames;
13901  ma_uint32 internalPeriods;
13902  ma_snd_pcm_hw_params_t* pHWParams;
13903  ma_snd_pcm_sw_params_t* pSWParams;
13904  ma_snd_pcm_uframes_t bufferBoundary;
13905  float bufferSizeScaleFactor;
13906 
13907  ma_assert(pContext != NULL);
13908  ma_assert(pConfig != NULL);
13909  ma_assert(deviceType != ma_device_type_duplex); /* This function should only be called for playback _or_ capture, never duplex. */
13910  ma_assert(pDevice != NULL);
13911 
13912  formatALSA = ma_convert_ma_format_to_alsa_format((deviceType == ma_device_type_capture) ? pConfig->capture.format : pConfig->playback.format);
13913  shareMode = (deviceType == ma_device_type_capture) ? pConfig->capture.shareMode : pConfig->playback.shareMode;
13914  pDeviceID = (deviceType == ma_device_type_capture) ? pConfig->capture.pDeviceID : pConfig->playback.pDeviceID;
13915 
13916  result = ma_context_open_pcm__alsa(pContext, shareMode, deviceType, pDeviceID, &pPCM);
13917  if (result != MA_SUCCESS) {
13918  return result;
13919  }
13920 
13921  /* If using the default buffer size we may want to apply some device-specific scaling for known devices that have peculiar latency characteristics */
13922  bufferSizeScaleFactor = 1;
13923  if (pDevice->usingDefaultBufferSize) {
13924  ma_snd_pcm_info_t* pInfo = (ma_snd_pcm_info_t*)calloc(1, ((ma_snd_pcm_info_sizeof_proc)pContext->alsa.snd_pcm_info_sizeof)());
13925  if (pInfo == NULL) {
13926  return MA_OUT_OF_MEMORY;
13927  }
13928 
13929  /* We may need to scale the size of the buffer depending on the device. */
13930  if (((ma_snd_pcm_info_proc)pContext->alsa.snd_pcm_info)(pPCM, pInfo) == 0) {
13931  const char* deviceName = ((ma_snd_pcm_info_get_name_proc)pContext->alsa.snd_pcm_info_get_name)(pInfo);
13932  if (deviceName != NULL) {
13933  if (ma_strcmp(deviceName, "default") == 0) {
13934  char** ppDeviceHints;
13935  char** ppNextDeviceHint;
13936 
13937  /* It's the default device. We need to use DESC from snd_device_name_hint(). */
13938  if (((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints) < 0) {
13939  ma_free(pInfo);
13940  return MA_NO_BACKEND;
13941  }
13942 
13943  ppNextDeviceHint = ppDeviceHints;
13944  while (*ppNextDeviceHint != NULL) {
13945  char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
13946  char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
13947  char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
13948 
13949  ma_bool32 foundDevice = MA_FALSE;
13950  if ((deviceType == ma_device_type_playback && (IOID == NULL || ma_strcmp(IOID, "Output") == 0)) ||
13951  (deviceType == ma_device_type_capture && (IOID != NULL && ma_strcmp(IOID, "Input" ) == 0))) {
13952  if (ma_strcmp(NAME, deviceName) == 0) {
13953  bufferSizeScaleFactor = ma_find_default_buffer_size_scale__alsa(DESC);
13954  foundDevice = MA_TRUE;
13955  }
13956  }
13957 
13958  free(NAME);
13959  free(DESC);
13960  free(IOID);
13961  ppNextDeviceHint += 1;
13962 
13963  if (foundDevice) {
13964  break;
13965  }
13966  }
13967 
13968  ((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
13969  } else {
13970  bufferSizeScaleFactor = ma_find_default_buffer_size_scale__alsa(deviceName);
13971  }
13972  }
13973  }
13974 
13975  ma_free(pInfo);
13976  }
13977 
13978 
13979  /* Hardware parameters. */
13980  pHWParams = (ma_snd_pcm_hw_params_t*)calloc(1, ((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)());
13981  if (pHWParams == NULL) {
13982  return MA_OUT_OF_MEMORY;
13983  }
13984 
13985  if (((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams) < 0) {
13986  ma_free(pHWParams);
13987  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
13988  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
13989  }
13990 
13991  /* MMAP Mode. Try using interleaved MMAP access. If this fails, fall back to standard readi/writei. */
13992  isUsingMMap = MA_FALSE;
13993 #if 0 /* NOTE: MMAP mode temporarily disabled. */
13994  if (deviceType != ma_device_type_capture) { /* <-- Disabling MMAP mode for capture devices because I apparently do not have a device that supports it which means I can't test it... Contributions welcome. */
13995  if (!pConfig->alsa.noMMap && ma_device__is_async(pDevice)) {
13996  if (((ma_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) {
13997  pDevice->alsa.isUsingMMap = MA_TRUE;
13998  }
13999  }
14000  }
14001 #endif
14002 
14003  if (!isUsingMMap) {
14004  if (((ma_snd_pcm_hw_params_set_access_proc)pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
14005  ma_free(pHWParams);
14006  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14007  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed.", MA_FORMAT_NOT_SUPPORTED);
14008  }
14009  }
14010 
14011  /*
14012  Most important properties first. The documentation for OSS (yes, I know this is ALSA!) recommends format, channels, then sample rate. I can't
14013  find any documentation for ALSA specifically, so I'm going to copy the recommendation for OSS.
14014  */
14015 
14016  /* Format. */
14017  {
14018  ma_snd_pcm_format_mask_t* pFormatMask;
14019 
14020  /* Try getting every supported format first. */
14021  pFormatMask = (ma_snd_pcm_format_mask_t*)calloc(1, ((ma_snd_pcm_format_mask_sizeof_proc)pContext->alsa.snd_pcm_format_mask_sizeof)());
14022  if (pFormatMask == NULL) {
14023  ma_free(pHWParams);
14024  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14025  return MA_OUT_OF_MEMORY;
14026  }
14027 
14028  ((ma_snd_pcm_hw_params_get_format_mask_proc)pContext->alsa.snd_pcm_hw_params_get_format_mask)(pHWParams, pFormatMask);
14029 
14030  /*
14031  At this point we should have a list of supported formats, so now we need to find the best one. We first check if the requested format is
14032  supported, and if so, use that one. If it's not supported, we just run though a list of formats and try to find the best one.
14033  */
14034  if (!((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, formatALSA)) {
14035  size_t i;
14036 
14037  /* The requested format is not supported so now try running through the list of formats and return the best one. */
14038  ma_snd_pcm_format_t preferredFormatsALSA[] = {
14039  MA_SND_PCM_FORMAT_S16_LE, /* ma_format_s16 */
14040  MA_SND_PCM_FORMAT_FLOAT_LE, /* ma_format_f32 */
14041  MA_SND_PCM_FORMAT_S32_LE, /* ma_format_s32 */
14042  MA_SND_PCM_FORMAT_S24_3LE, /* ma_format_s24 */
14043  MA_SND_PCM_FORMAT_U8 /* ma_format_u8 */
14044  };
14045 
14046  if (ma_is_big_endian()) {
14047  preferredFormatsALSA[0] = MA_SND_PCM_FORMAT_S16_BE;
14048  preferredFormatsALSA[1] = MA_SND_PCM_FORMAT_FLOAT_BE;
14049  preferredFormatsALSA[2] = MA_SND_PCM_FORMAT_S32_BE;
14050  preferredFormatsALSA[3] = MA_SND_PCM_FORMAT_S24_3BE;
14051  preferredFormatsALSA[4] = MA_SND_PCM_FORMAT_U8;
14052  }
14053 
14054  formatALSA = MA_SND_PCM_FORMAT_UNKNOWN;
14055  for (i = 0; i < (sizeof(preferredFormatsALSA) / sizeof(preferredFormatsALSA[0])); ++i) {
14056  if (((ma_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, preferredFormatsALSA[i])) {
14057  formatALSA = preferredFormatsALSA[i];
14058  break;
14059  }
14060  }
14061 
14062  if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) {
14063  ma_free(pHWParams);
14064  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14065  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats.", MA_FORMAT_NOT_SUPPORTED);
14066  }
14067  }
14068 
14069  ma_free(pFormatMask);
14070  pFormatMask = NULL;
14071 
14072  if (((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA) < 0) {
14073  ma_free(pHWParams);
14074  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14075  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.", MA_FORMAT_NOT_SUPPORTED);
14076  }
14077 
14078  internalFormat = ma_format_from_alsa(formatALSA);
14079  if (internalFormat == ma_format_unknown) {
14080  ma_free(pHWParams);
14081  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14082  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
14083  }
14084  }
14085 
14086  /* Channels. */
14087  {
14088  unsigned int channels = (deviceType == ma_device_type_capture) ? pConfig->capture.channels : pConfig->playback.channels;
14089  if (((ma_snd_pcm_hw_params_set_channels_near_proc)pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels) < 0) {
14090  ma_free(pHWParams);
14091  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14092  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed.", MA_FORMAT_NOT_SUPPORTED);
14093  }
14094  internalChannels = (ma_uint32)channels;
14095  }
14096 
14097  /* Sample Rate */
14098  {
14099  unsigned int sampleRate;
14100 
14101  /*
14102  It appears there's either a bug in ALSA, a bug in some drivers, or I'm doing something silly; but having resampling enabled causes
14103  problems with some device configurations when used in conjunction with MMAP access mode. To fix this problem we need to disable
14104  resampling.
14105 
14106  To reproduce this problem, open the "plug:dmix" device, and set the sample rate to 44100. Internally, it looks like dmix uses a
14107  sample rate of 48000. The hardware parameters will get set correctly with no errors, but it looks like the 44100 -> 48000 resampling
14108  doesn't work properly - but only with MMAP access mode. You will notice skipping/crackling in the audio, and it'll run at a slightly
14109  faster rate.
14110 
14111  miniaudio has built-in support for sample rate conversion (albeit low quality at the moment), so disabling resampling should be fine
14112  for us. The only problem is that it won't be taking advantage of any kind of hardware-accelerated resampling and it won't be very
14113  good quality until I get a chance to improve the quality of miniaudio's software sample rate conversion.
14114 
14115  I don't currently know if the dmix plugin is the only one with this error. Indeed, this is the only one I've been able to reproduce
14116  this error with. In the future, we may want to restrict the disabling of resampling to only known bad plugins.
14117  */
14118  ((ma_snd_pcm_hw_params_set_rate_resample_proc)pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0);
14119 
14120  sampleRate = pConfig->sampleRate;
14121  if (((ma_snd_pcm_hw_params_set_rate_near_proc)pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0) < 0) {
14122  ma_free(pHWParams);
14123  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14124  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed.", MA_FORMAT_NOT_SUPPORTED);
14125  }
14126  internalSampleRate = (ma_uint32)sampleRate;
14127  }
14128 
14129  /* Buffer Size */
14130  {
14131  ma_snd_pcm_uframes_t actualBufferSizeInFrames = pConfig->bufferSizeInFrames;
14132  if (actualBufferSizeInFrames == 0) {
14133  actualBufferSizeInFrames = ma_scale_buffer_size(ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, internalSampleRate), bufferSizeScaleFactor);
14134  }
14135 
14136  if (((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames) < 0) {
14137  ma_free(pHWParams);
14138  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14139  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed.", MA_FORMAT_NOT_SUPPORTED);
14140  }
14141  internalBufferSizeInFrames = actualBufferSizeInFrames;
14142  }
14143 
14144  /* Periods. */
14145  {
14146  ma_uint32 periods = pConfig->periods;
14147  if (((ma_snd_pcm_hw_params_set_periods_near_proc)pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL) < 0) {
14148  ma_free(pHWParams);
14149  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14150  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed.", MA_FORMAT_NOT_SUPPORTED);
14151  }
14152  internalPeriods = periods;
14153  }
14154 
14155  /* Apply hardware parameters. */
14156  if (((ma_snd_pcm_hw_params_proc)pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams) < 0) {
14157  ma_free(pHWParams);
14158  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14159  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
14160  }
14161 
14162  ma_free(pHWParams);
14163  pHWParams = NULL;
14164 
14165 
14166  /* Software parameters. */
14167  pSWParams = (ma_snd_pcm_sw_params_t*)calloc(1, ((ma_snd_pcm_sw_params_sizeof_proc)pContext->alsa.snd_pcm_sw_params_sizeof)());
14168  if (pSWParams == NULL) {
14169  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14170  return MA_OUT_OF_MEMORY;
14171  }
14172 
14173  if (((ma_snd_pcm_sw_params_current_proc)pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams) != 0) {
14174  ma_free(pSWParams);
14175  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14176  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
14177  }
14178 
14179  if (((ma_snd_pcm_sw_params_set_avail_min_proc)pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, ma_prev_power_of_2(internalBufferSizeInFrames/internalPeriods)) != 0) {
14180  ma_free(pSWParams);
14181  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14182  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.", MA_FORMAT_NOT_SUPPORTED);
14183  }
14184 
14185  if (((ma_snd_pcm_sw_params_get_boundary_proc)pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary) < 0) {
14186  bufferBoundary = internalBufferSizeInFrames;
14187  }
14188 
14189  /*printf("TRACE: bufferBoundary=%ld\n", bufferBoundary);*/
14190 
14191  if (deviceType == ma_device_type_playback && !isUsingMMap) { /* Only playback devices in writei/readi mode need a start threshold. */
14192  /*
14193  Subtle detail here with the start threshold. When in playback-only mode (no full-duplex) we can set the start threshold to
14194  the size of a period. But for full-duplex we need to set it such that it is at least two periods.
14195  */
14196  if (((ma_snd_pcm_sw_params_set_start_threshold_proc)pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, (internalBufferSizeInFrames/internalPeriods)*2) != 0) {
14197  ma_free(pSWParams);
14198  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14199  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
14200  }
14201  if (((ma_snd_pcm_sw_params_set_stop_threshold_proc)pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary) != 0) { /* Set to boundary to loop instead of stop in the event of an xrun. */
14202  ma_free(pSWParams);
14203  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14204  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
14205  }
14206  }
14207 
14208  if (((ma_snd_pcm_sw_params_proc)pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams) != 0) {
14209  ma_free(pSWParams);
14210  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14211  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.", MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE);
14212  }
14213 
14214  ma_free(pSWParams);
14215  pSWParams = NULL;
14216 
14217 
14218  /* Grab the internal channel map. For now we're not going to bother trying to change the channel map and instead just do it ourselves. */
14219  {
14220  ma_snd_pcm_chmap_t* pChmap = ((ma_snd_pcm_get_chmap_proc)pContext->alsa.snd_pcm_get_chmap)(pPCM);
14221  if (pChmap != NULL) {
14222  ma_uint32 iChannel;
14223 
14224  /* There are cases where the returned channel map can have a different channel count than was returned by snd_pcm_hw_params_set_channels_near(). */
14225  if (pChmap->channels >= internalChannels) {
14226  /* Drop excess channels. */
14227  for (iChannel = 0; iChannel < internalChannels; ++iChannel) {
14228  internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
14229  }
14230  } else {
14231  ma_uint32 i;
14232 
14233  /*
14234  Excess channels use defaults. Do an initial fill with defaults, overwrite the first pChmap->channels, validate to ensure there are no duplicate
14235  channels. If validation fails, fall back to defaults.
14236  */
14237  ma_bool32 isValid = MA_TRUE;
14238 
14239  /* Fill with defaults. */
14240  ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
14241 
14242  /* Overwrite first pChmap->channels channels. */
14243  for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) {
14244  internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
14245  }
14246 
14247  /* Validate. */
14248  for (i = 0; i < internalChannels && isValid; ++i) {
14249  ma_uint32 j;
14250  for (j = i+1; j < internalChannels; ++j) {
14251  if (internalChannelMap[i] == internalChannelMap[j]) {
14252  isValid = MA_FALSE;
14253  break;
14254  }
14255  }
14256  }
14257 
14258  /* If our channel map is invalid, fall back to defaults. */
14259  if (!isValid) {
14260  ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
14261  }
14262  }
14263 
14264  free(pChmap);
14265  pChmap = NULL;
14266  } else {
14267  /* Could not retrieve the channel map. Fall back to a hard-coded assumption. */
14268  ma_get_standard_channel_map(ma_standard_channel_map_alsa, internalChannels, internalChannelMap);
14269  }
14270  }
14271 
14272 
14273  /* We're done. Prepare the device. */
14274  if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM) < 0) {
14275  ((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
14276  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.", MA_FAILED_TO_START_BACKEND_DEVICE);
14277  }
14278 
14279 
14280  if (deviceType == ma_device_type_capture) {
14281  pDevice->alsa.pPCMCapture = (ma_ptr)pPCM;
14282  pDevice->alsa.isUsingMMapCapture = isUsingMMap;
14283  pDevice->capture.internalFormat = internalFormat;
14284  pDevice->capture.internalChannels = internalChannels;
14285  pDevice->capture.internalSampleRate = internalSampleRate;
14286  ma_channel_map_copy(pDevice->capture.internalChannelMap, internalChannelMap, internalChannels);
14287  pDevice->capture.internalBufferSizeInFrames = internalBufferSizeInFrames;
14288  pDevice->capture.internalPeriods = internalPeriods;
14289  } else {
14290  pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM;
14291  pDevice->alsa.isUsingMMapPlayback = isUsingMMap;
14292  pDevice->playback.internalFormat = internalFormat;
14293  pDevice->playback.internalChannels = internalChannels;
14294  pDevice->playback.internalSampleRate = internalSampleRate;
14295  ma_channel_map_copy(pDevice->playback.internalChannelMap, internalChannelMap, internalChannels);
14296  pDevice->playback.internalBufferSizeInFrames = internalBufferSizeInFrames;
14297  pDevice->playback.internalPeriods = internalPeriods;
14298  }
14299 
14300  return MA_SUCCESS;
14301 }
14302 
14303 ma_result ma_device_init__alsa(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
14304 {
14305  ma_assert(pDevice != NULL);
14306 
14307  ma_zero_object(&pDevice->alsa);
14308 
14309  if (pConfig->deviceType == ma_device_type_loopback) {
14311  }
14312 
14313  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
14314  ma_result result = ma_device_init_by_type__alsa(pContext, pConfig, ma_device_type_capture, pDevice);
14315  if (result != MA_SUCCESS) {
14316  return result;
14317  }
14318  }
14319 
14320  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
14321  ma_result result = ma_device_init_by_type__alsa(pContext, pConfig, ma_device_type_playback, pDevice);
14322  if (result != MA_SUCCESS) {
14323  return result;
14324  }
14325  }
14326 
14327  return MA_SUCCESS;
14328 }
14329 
14330 #if 0
14331 ma_result ma_device_start__alsa(ma_device* pDevice)
14332 {
14333  ma_assert(pDevice != NULL);
14334 
14335  /* Prepare the device first... */
14336  if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
14337  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.", MA_FAILED_TO_START_BACKEND_DEVICE);
14338  }
14339 
14340  /*
14341  ... and then grab an initial chunk from the client. After this is done, the device should
14342  automatically start playing, since that's how we configured the software parameters.
14343  */
14344  if (pDevice->type == ma_device_type_playback) {
14345  if (!ma_device_read_from_client_and_write__alsa(pDevice)) {
14346  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to write initial chunk of data to the playback device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
14347  }
14348 
14349  /* mmap mode requires an explicit start. */
14350  if (pDevice->alsa.isUsingMMap) {
14351  if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
14352  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device.", MA_FAILED_TO_START_BACKEND_DEVICE);
14353  }
14354  }
14355  } else {
14356  if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCM) < 0) {
14357  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device.", MA_FAILED_TO_START_BACKEND_DEVICE);
14358  }
14359  }
14360 
14361  return MA_SUCCESS;
14362 }
14363 
14364 ma_result ma_device_break_main_loop__alsa(ma_device* pDevice)
14365 {
14366  ma_assert(pDevice != NULL);
14367 
14368  pDevice->alsa.breakFromMainLoop = MA_TRUE;
14369  return MA_SUCCESS;
14370 }
14371 
14372 ma_result ma_device_main_loop__alsa(ma_device* pDevice)
14373 {
14374  ma_assert(pDevice != NULL);
14375 
14376  pDevice->alsa.breakFromMainLoop = MA_FALSE;
14377  if (pDevice->type == ma_device_type_playback) {
14378  /* Playback. Read from client, write to device. */
14379  while (!pDevice->alsa.breakFromMainLoop && ma_device_read_from_client_and_write__alsa(pDevice)) {
14380  }
14381  } else {
14382  /* Capture. Read from device, write to client. */
14383  while (!pDevice->alsa.breakFromMainLoop && ma_device_read_and_send_to_client__alsa(pDevice)) {
14384  }
14385  }
14386 
14387  return MA_SUCCESS;
14388 }
14389 #endif /* 0 */
14390 
14391 ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead)
14392 {
14393  ma_snd_pcm_sframes_t resultALSA;
14394 
14395  ma_assert(pDevice != NULL);
14396  ma_assert(pFramesOut != NULL);
14397 
14398  if (pFramesRead != NULL) {
14399  *pFramesRead = 0;
14400  }
14401 
14402  for (;;) {
14403  resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
14404  if (resultALSA >= 0) {
14405  break; /* Success. */
14406  } else {
14407  if (resultALSA == -EAGAIN) {
14408  /*printf("TRACE: EGAIN (read)\n");*/
14409  continue; /* Try again. */
14410  } else if (resultALSA == -EPIPE) {
14411  /*printf("TRACE: EPIPE (read)\n");*/
14412 
14413  /* Overrun. Recover and try again. If this fails we need to return an error. */
14414  if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE) < 0) {
14415  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
14416  }
14417 
14418  if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
14419  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
14420  }
14421 
14422  resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
14423  if (resultALSA < 0) {
14424  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to read data from the internal device.", MA_FAILED_TO_READ_DATA_FROM_DEVICE);
14425  }
14426  }
14427  }
14428  }
14429 
14430  if (pFramesRead != NULL) {
14431  *pFramesRead = resultALSA;
14432  }
14433 
14434  return MA_SUCCESS;
14435 }
14436 
14437 ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
14438 {
14439  ma_snd_pcm_sframes_t resultALSA;
14440 
14441  ma_assert(pDevice != NULL);
14442  ma_assert(pFrames != NULL);
14443 
14444  if (pFramesWritten != NULL) {
14445  *pFramesWritten = 0;
14446  }
14447 
14448  for (;;) {
14449  resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
14450  if (resultALSA >= 0) {
14451  break; /* Success. */
14452  } else {
14453  if (resultALSA == -EAGAIN) {
14454  /*printf("TRACE: EGAIN (write)\n");*/
14455  continue; /* Try again. */
14456  } else if (resultALSA == -EPIPE) {
14457  /*printf("TRACE: EPIPE (write)\n");*/
14458 
14459  /* Underrun. Recover and try again. If this fails we need to return an error. */
14460  if (((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE) < 0) { /* MA_TRUE=silent (don't print anything on error). */
14461  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
14462  }
14463 
14464  /*
14465  In my testing I have had a situation where writei() does not automatically restart the device even though I've set it
14466  up as such in the software parameters. What will happen is writei() will block indefinitely even though the number of
14467  frames is well beyond the auto-start threshold. To work around this I've needed to add an explicit start here. Not sure
14468  if this is me just being stupid and not recovering the device properly, but this definitely feels like something isn't
14469  quite right here.
14470  */
14471  if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
14472  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
14473  }
14474 
14475  resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
14476  if (resultALSA < 0) {
14477  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to write data to device after underrun.", MA_FAILED_TO_START_BACKEND_DEVICE);
14478  }
14479  }
14480  }
14481  }
14482 
14483  if (pFramesWritten != NULL) {
14484  *pFramesWritten = resultALSA;
14485  }
14486 
14487  return MA_SUCCESS;
14488 }
14489 
14490 ma_result ma_device_main_loop__alsa(ma_device* pDevice)
14491 {
14493  ma_bool32 exitLoop = MA_FALSE;
14494 
14495  ma_assert(pDevice != NULL);
14496 
14497  /* Capture devices need to be started immediately. */
14498  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
14499  if (((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
14500  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device in preparation for reading.", MA_FAILED_TO_START_BACKEND_DEVICE);
14501  }
14502  }
14503 
14504  while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
14505  switch (pDevice->type)
14506  {
14507  case ma_device_type_duplex:
14508  {
14509  if (pDevice->alsa.isUsingMMapCapture || pDevice->alsa.isUsingMMapPlayback) {
14510  /* MMAP */
14511  return MA_INVALID_OPERATION; /* Not yet implemented. */
14512  } else {
14513  /* readi() and writei() */
14514 
14515  /* The process is: device_read -> convert -> callback -> convert -> device_write */
14516  ma_uint8 capturedDeviceData[8192];
14517  ma_uint8 playbackDeviceData[8192];
14518  ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
14519  ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
14520 
14521  ma_uint32 totalFramesProcessed = 0;
14522  ma_uint32 periodSizeInFrames = ma_min(pDevice->capture.internalBufferSizeInFrames/pDevice->capture.internalPeriods, pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods);
14523 
14524  while (totalFramesProcessed < periodSizeInFrames) {
14525  ma_uint32 framesRemaining = periodSizeInFrames - totalFramesProcessed;
14526  ma_uint32 framesProcessed;
14527  ma_uint32 framesToProcess = framesRemaining;
14528  if (framesToProcess > capturedDeviceDataCapInFrames) {
14529  framesToProcess = capturedDeviceDataCapInFrames;
14530  }
14531 
14532  result = ma_device_read__alsa(pDevice, capturedDeviceData, framesToProcess, &framesProcessed);
14533  if (result != MA_SUCCESS) {
14534  exitLoop = MA_TRUE;
14535  break;
14536  }
14537 
14538  pDevice->capture._dspFrameCount = framesToProcess;
14539  pDevice->capture._dspFrames = capturedDeviceData;
14540 
14541  for (;;) {
14542  ma_uint8 capturedData[8192];
14543  ma_uint8 playbackData[8192];
14544  ma_uint32 capturedDataCapInFrames = sizeof(capturedData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
14545  ma_uint32 playbackDataCapInFrames = sizeof(playbackData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
14546 
14547  ma_uint32 capturedFramesToTryProcessing = ma_min(capturedDataCapInFrames, playbackDataCapInFrames);
14548  ma_uint32 capturedFramesToProcess = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, capturedData, capturedFramesToTryProcessing);
14549  if (capturedFramesToProcess == 0) {
14550  break; /* Don't fire the data callback with zero frames. */
14551  }
14552 
14553  ma_device__on_data(pDevice, playbackData, capturedData, capturedFramesToProcess);
14554 
14555  /* At this point the playbackData buffer should be holding data that needs to be written to the device. */
14556  pDevice->playback._dspFrameCount = capturedFramesToProcess;
14557  pDevice->playback._dspFrames = playbackData;
14558  for (;;) {
14559  ma_uint32 playbackDeviceFramesCount = (ma_uint32)ma_pcm_converter_read(&pDevice->playback.converter, playbackDeviceData, playbackDeviceDataCapInFrames);
14560  if (playbackDeviceFramesCount == 0) {
14561  break;
14562  }
14563 
14564  result = ma_device_write__alsa(pDevice, playbackDeviceData, playbackDeviceFramesCount, NULL);
14565  if (result != MA_SUCCESS) {
14566  exitLoop = MA_TRUE;
14567  break;
14568  }
14569 
14570  if (playbackDeviceFramesCount < playbackDeviceDataCapInFrames) {
14571  break;
14572  }
14573  }
14574 
14575  if (capturedFramesToProcess < capturedFramesToTryProcessing) {
14576  break;
14577  }
14578 
14579  /* In case an error happened from ma_device_write2__alsa()... */
14580  if (result != MA_SUCCESS) {
14581  exitLoop = MA_TRUE;
14582  break;
14583  }
14584  }
14585 
14586  totalFramesProcessed += framesProcessed;
14587  }
14588  }
14589  } break;
14590 
14592  {
14593  if (pDevice->alsa.isUsingMMapCapture) {
14594  /* MMAP */
14595  return MA_INVALID_OPERATION; /* Not yet implemented. */
14596  } else {
14597  /* readi() */
14598 
14599  /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
14600  ma_uint8 intermediaryBuffer[8192];
14601  ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
14602  ma_uint32 periodSizeInFrames = pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods;
14603  ma_uint32 framesReadThisPeriod = 0;
14604  while (framesReadThisPeriod < periodSizeInFrames) {
14605  ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
14606  ma_uint32 framesProcessed;
14607  ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
14608  if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
14609  framesToReadThisIteration = intermediaryBufferSizeInFrames;
14610  }
14611 
14612  result = ma_device_read__alsa(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
14613  if (result != MA_SUCCESS) {
14614  exitLoop = MA_TRUE;
14615  break;
14616  }
14617 
14618  ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
14619 
14620  framesReadThisPeriod += framesProcessed;
14621  }
14622  }
14623  } break;
14624 
14626  {
14627  if (pDevice->alsa.isUsingMMapPlayback) {
14628  /* MMAP */
14629  return MA_INVALID_OPERATION; /* Not yet implemented. */
14630  } else {
14631  /* writei() */
14632 
14633  /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
14634  ma_uint8 intermediaryBuffer[8192];
14635  ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
14636  ma_uint32 periodSizeInFrames = pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods;
14637  ma_uint32 framesWrittenThisPeriod = 0;
14638  while (framesWrittenThisPeriod < periodSizeInFrames) {
14639  ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
14640  ma_uint32 framesProcessed;
14641  ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
14642  if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
14643  framesToWriteThisIteration = intermediaryBufferSizeInFrames;
14644  }
14645 
14646  ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
14647 
14648  result = ma_device_write__alsa(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
14649  if (result != MA_SUCCESS) {
14650  exitLoop = MA_TRUE;
14651  break;
14652  }
14653 
14654  framesWrittenThisPeriod += framesProcessed;
14655  }
14656  }
14657  } break;
14658 
14659  /* To silence a warning. Will never hit this. */
14661  default: break;
14662  }
14663  }
14664 
14665  /* Here is where the device needs to be stopped. */
14666  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
14667  ((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
14668 
14669  /* We need to prepare the device again, otherwise we won't be able to restart the device. */
14670  if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
14671  #ifdef MA_DEBUG_OUTPUT
14672  printf("[ALSA] Failed to prepare capture device after stopping.\n");
14673  #endif
14674  }
14675  }
14676 
14677  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
14678  ((ma_snd_pcm_drain_proc)pDevice->pContext->alsa.snd_pcm_drain)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
14679 
14680  /* We need to prepare the device again, otherwise we won't be able to restart the device. */
14681  if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
14682  #ifdef MA_DEBUG_OUTPUT
14683  printf("[ALSA] Failed to prepare playback device after stopping.\n");
14684  #endif
14685  }
14686  }
14687 
14688  return result;
14689 }
14690 
14691 ma_result ma_context_uninit__alsa(ma_context* pContext)
14692 {
14693  ma_assert(pContext != NULL);
14694  ma_assert(pContext->backend == ma_backend_alsa);
14695 
14696  /* Clean up memory for memory leak checkers. */
14697  ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)();
14698 
14699 #ifndef MA_NO_RUNTIME_LINKING
14700  ma_dlclose(pContext, pContext->alsa.asoundSO);
14701 #endif
14702 
14703  ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock);
14704 
14705  return MA_SUCCESS;
14706 }
14707 
14708 ma_result ma_context_init__alsa(const ma_context_config* pConfig, ma_context* pContext)
14709 {
14710 #ifndef MA_NO_RUNTIME_LINKING
14711  const char* libasoundNames[] = {
14712  "libasound.so.2",
14713  "libasound.so"
14714  };
14715  size_t i;
14716 
14717  for (i = 0; i < ma_countof(libasoundNames); ++i) {
14718  pContext->alsa.asoundSO = ma_dlopen(pContext, libasoundNames[i]);
14719  if (pContext->alsa.asoundSO != NULL) {
14720  break;
14721  }
14722  }
14723 
14724  if (pContext->alsa.asoundSO == NULL) {
14725 #ifdef MA_DEBUG_OUTPUT
14726  printf("[ALSA] Failed to open shared object.\n");
14727 #endif
14728  return MA_NO_BACKEND;
14729  }
14730 
14731  pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_open");
14732  pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_close");
14733  pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof");
14734  pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_any");
14735  pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format");
14736  pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first");
14737  pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask");
14738  pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near");
14739  pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample");
14740  pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near");
14741  pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near");
14742  pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near");
14743  pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access");
14744  pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format");
14745  pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels");
14746  pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min");
14747  pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max");
14748  pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate");
14749  pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min");
14750  pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max");
14751  pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size");
14752  pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods");
14753  pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access");
14754  pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params");
14755  pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof");
14756  pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_current");
14757  pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary");
14758  pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min");
14759  pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold");
14760  pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold");
14761  pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params");
14762  pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof");
14763  pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_test");
14764  pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_get_chmap");
14765  pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_state");
14766  pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_prepare");
14767  pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start");
14768  pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop");
14769  pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain");
14770  pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint");
14771  pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint");
14772  pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index");
14773  pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_free_hint");
14774  pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_begin");
14775  pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_commit");
14776  pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_recover");
14777  pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_readi");
14778  pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_writei");
14779  pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail");
14780  pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update");
14781  pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait");
14782  pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info");
14783  pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof");
14784  pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name");
14785  pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global");
14786 #else
14787  /* The system below is just for type safety. */
14788  ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open;
14789  ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close;
14790  ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof;
14791  ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any;
14792  ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format;
14793  ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first;
14794  ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask;
14795  ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near;
14796  ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample;
14797  ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near;
14798  ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near;
14799  ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near;
14800  ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access;
14801  ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format;
14802  ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels;
14803  ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min;
14804  ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max;
14805  ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate;
14806  ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min;
14807  ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max;
14808  ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size;
14809  ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods;
14810  ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access;
14811  ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params;
14812  ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof;
14813  ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current;
14814  ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary;
14815  ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min;
14816  ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold;
14817  ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold;
14818  ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params;
14819  ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof;
14820  ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test;
14821  ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap;
14822  ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state;
14823  ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare;
14824  ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start;
14825  ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop;
14826  ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain;
14827  ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint;
14828  ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint;
14829  ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index;
14830  ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint;
14831  ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin;
14832  ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit;
14833  ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover;
14834  ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi;
14835  ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei;
14836  ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail;
14837  ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update;
14838  ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait;
14839  ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info;
14840  ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof;
14841  ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name;
14842  ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global;
14843 
14844  pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open;
14845  pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close;
14846  pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof;
14847  pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any;
14848  pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format;
14849  pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first;
14850  pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask;
14851  pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near;
14852  pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample;
14853  pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near;
14854  pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near;
14855  pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near;
14856  pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access;
14857  pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format;
14858  pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels;
14859  pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min;
14860  pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max;
14861  pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate;
14862  pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size;
14863  pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods;
14864  pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access;
14865  pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params;
14866  pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof;
14867  pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current;
14868  pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary;
14869  pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min;
14870  pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold;
14871  pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold;
14872  pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params;
14873  pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof;
14874  pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test;
14875  pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap;
14876  pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state;
14877  pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare;
14878  pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start;
14879  pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop;
14880  pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain;
14881  pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint;
14882  pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint;
14883  pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index;
14884  pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint;
14885  pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin;
14886  pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit;
14887  pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover;
14888  pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi;
14889  pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei;
14890  pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail;
14891  pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update;
14892  pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait;
14893  pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info;
14894  pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof;
14895  pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name;
14896  pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global;
14897 #endif
14898 
14899  pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration;
14900 
14901  if (ma_mutex_init(pContext, &pContext->alsa.internalDeviceEnumLock) != MA_SUCCESS) {
14902  ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration.", MA_ERROR);
14903  }
14904 
14905  pContext->onUninit = ma_context_uninit__alsa;
14906  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__alsa;
14907  pContext->onEnumDevices = ma_context_enumerate_devices__alsa;
14908  pContext->onGetDeviceInfo = ma_context_get_device_info__alsa;
14909  pContext->onDeviceInit = ma_device_init__alsa;
14910  pContext->onDeviceUninit = ma_device_uninit__alsa;
14911  pContext->onDeviceStart = NULL; /* Not used. Started in the main loop. */
14912  pContext->onDeviceStop = NULL; /* Not used. Started in the main loop. */
14913  pContext->onDeviceMainLoop = ma_device_main_loop__alsa;
14914 
14915  return MA_SUCCESS;
14916 }
14917 #endif /* ALSA */
14918 
14919 
14920 
14921 /******************************************************************************
14922 
14923 PulseAudio Backend
14924 
14925 ******************************************************************************/
14926 #ifdef MA_HAS_PULSEAUDIO
14927 /*
14928 It is assumed pulseaudio.h is available when compile-time linking is being used. We use this for type safety when using
14929 compile time linking (we don't have this luxury when using runtime linking without headers).
14930 
14931 When using compile time linking, each of our ma_* equivalents should use the sames types as defined by the header. The
14932 reason for this is that it allow us to take advantage of proper type safety.
14933 */
14934 #ifdef MA_NO_RUNTIME_LINKING
14935 #include <pulse/pulseaudio.h>
14936 
14937 #define MA_PA_OK PA_OK
14938 #define MA_PA_ERR_ACCESS PA_ERR_ACCESS
14939 #define MA_PA_ERR_INVALID PA_ERR_INVALID
14940 #define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY
14941 
14942 #define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX
14943 #define MA_PA_RATE_MAX PA_RATE_MAX
14944 
14945 typedef pa_context_flags_t ma_pa_context_flags_t;
14946 #define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS
14947 #define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN
14948 #define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
14949 
14950 typedef pa_stream_flags_t ma_pa_stream_flags_t;
14951 #define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS
14952 #define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED
14953 #define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING
14954 #define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC
14955 #define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE
14956 #define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS
14957 #define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS
14958 #define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT
14959 #define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE
14960 #define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS
14961 #define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE
14962 #define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE
14963 #define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT
14964 #define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED
14965 #define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY
14966 #define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
14967 #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
14968 #define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
14969 #define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
14970 #define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
14971 #define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
14972 
14973 typedef pa_sink_flags_t ma_pa_sink_flags_t;
14974 #define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS
14975 #define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL
14976 #define MA_PA_SINK_LATENCY PA_SINK_LATENCY
14977 #define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE
14978 #define MA_PA_SINK_NETWORK PA_SINK_NETWORK
14979 #define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL
14980 #define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
14981 #define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
14982 #define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY
14983 #define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS
14984 
14985 typedef pa_source_flags_t ma_pa_source_flags_t;
14986 #define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS
14987 #define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL
14988 #define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY
14989 #define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE
14990 #define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK
14991 #define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL
14992 #define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
14993 #define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY
14994 #define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME
14995 
14996 typedef pa_context_state_t ma_pa_context_state_t;
14997 #define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED
14998 #define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING
14999 #define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING
15000 #define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME
15001 #define MA_PA_CONTEXT_READY PA_CONTEXT_READY
15002 #define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED
15003 #define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED
15004 
15005 typedef pa_stream_state_t ma_pa_stream_state_t;
15006 #define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED
15007 #define MA_PA_STREAM_CREATING PA_STREAM_CREATING
15008 #define MA_PA_STREAM_READY PA_STREAM_READY
15009 #define MA_PA_STREAM_FAILED PA_STREAM_FAILED
15010 #define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED
15011 
15012 typedef pa_operation_state_t ma_pa_operation_state_t;
15013 #define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING
15014 #define MA_PA_OPERATION_DONE PA_OPERATION_DONE
15015 #define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED
15016 
15017 typedef pa_sink_state_t ma_pa_sink_state_t;
15018 #define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE
15019 #define MA_PA_SINK_RUNNING PA_SINK_RUNNING
15020 #define MA_PA_SINK_IDLE PA_SINK_IDLE
15021 #define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED
15022 
15023 typedef pa_source_state_t ma_pa_source_state_t;
15024 #define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE
15025 #define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING
15026 #define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE
15027 #define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED
15028 
15029 typedef pa_seek_mode_t ma_pa_seek_mode_t;
15030 #define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE
15031 #define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE
15032 #define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ
15033 #define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END
15034 
15035 typedef pa_channel_position_t ma_pa_channel_position_t;
15036 #define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID
15037 #define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO
15038 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT
15039 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT
15040 #define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER
15041 #define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER
15042 #define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT
15043 #define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT
15044 #define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE
15045 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
15046 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
15047 #define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT
15048 #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT
15049 #define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0
15050 #define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1
15051 #define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2
15052 #define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3
15053 #define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4
15054 #define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5
15055 #define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6
15056 #define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7
15057 #define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8
15058 #define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9
15059 #define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10
15060 #define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11
15061 #define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12
15062 #define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13
15063 #define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14
15064 #define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15
15065 #define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16
15066 #define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17
15067 #define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18
15068 #define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19
15069 #define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20
15070 #define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21
15071 #define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22
15072 #define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23
15073 #define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24
15074 #define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25
15075 #define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26
15076 #define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27
15077 #define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28
15078 #define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29
15079 #define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30
15080 #define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31
15081 #define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER
15082 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT
15083 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
15084 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER
15085 #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT
15086 #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT
15087 #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER
15088 #define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT
15089 #define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT
15090 #define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER
15091 #define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER
15092 
15093 typedef pa_channel_map_def_t ma_pa_channel_map_def_t;
15094 #define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF
15095 #define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA
15096 #define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX
15097 #define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX
15098 #define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS
15099 #define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT
15100 
15101 typedef pa_sample_format_t ma_pa_sample_format_t;
15102 #define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID
15103 #define MA_PA_SAMPLE_U8 PA_SAMPLE_U8
15104 #define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW
15105 #define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW
15106 #define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE
15107 #define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE
15108 #define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE
15109 #define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE
15110 #define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE
15111 #define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE
15112 #define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE
15113 #define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE
15114 #define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE
15115 #define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE
15116 
15117 typedef pa_mainloop ma_pa_mainloop;
15118 typedef pa_mainloop_api ma_pa_mainloop_api;
15119 typedef pa_context ma_pa_context;
15120 typedef pa_operation ma_pa_operation;
15121 typedef pa_stream ma_pa_stream;
15122 typedef pa_spawn_api ma_pa_spawn_api;
15123 typedef pa_buffer_attr ma_pa_buffer_attr;
15124 typedef pa_channel_map ma_pa_channel_map;
15125 typedef pa_cvolume ma_pa_cvolume;
15126 typedef pa_sample_spec ma_pa_sample_spec;
15127 typedef pa_sink_info ma_pa_sink_info;
15128 typedef pa_source_info ma_pa_source_info;
15129 
15130 typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t;
15131 typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t;
15132 typedef pa_source_info_cb_t ma_pa_source_info_cb_t;
15133 typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t;
15134 typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t;
15135 typedef pa_free_cb_t ma_pa_free_cb_t;
15136 #else
15137 #define MA_PA_OK 0
15138 #define MA_PA_ERR_ACCESS 1
15139 #define MA_PA_ERR_INVALID 2
15140 #define MA_PA_ERR_NOENTITY 5
15141 
15142 #define MA_PA_CHANNELS_MAX 32
15143 #define MA_PA_RATE_MAX 384000
15144 
15145 typedef int ma_pa_context_flags_t;
15146 #define MA_PA_CONTEXT_NOFLAGS 0x00000000
15147 #define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001
15148 #define MA_PA_CONTEXT_NOFAIL 0x00000002
15149 
15150 typedef int ma_pa_stream_flags_t;
15151 #define MA_PA_STREAM_NOFLAGS 0x00000000
15152 #define MA_PA_STREAM_START_CORKED 0x00000001
15153 #define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002
15154 #define MA_PA_STREAM_NOT_MONOTONIC 0x00000004
15155 #define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008
15156 #define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010
15157 #define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020
15158 #define MA_PA_STREAM_FIX_FORMAT 0x00000040
15159 #define MA_PA_STREAM_FIX_RATE 0x00000080
15160 #define MA_PA_STREAM_FIX_CHANNELS 0x00000100
15161 #define MA_PA_STREAM_DONT_MOVE 0x00000200
15162 #define MA_PA_STREAM_VARIABLE_RATE 0x00000400
15163 #define MA_PA_STREAM_PEAK_DETECT 0x00000800
15164 #define MA_PA_STREAM_START_MUTED 0x00001000
15165 #define MA_PA_STREAM_ADJUST_LATENCY 0x00002000
15166 #define MA_PA_STREAM_EARLY_REQUESTS 0x00004000
15167 #define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000
15168 #define MA_PA_STREAM_START_UNMUTED 0x00010000
15169 #define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000
15170 #define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000
15171 #define MA_PA_STREAM_PASSTHROUGH 0x00080000
15172 
15173 typedef int ma_pa_sink_flags_t;
15174 #define MA_PA_SINK_NOFLAGS 0x00000000
15175 #define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001
15176 #define MA_PA_SINK_LATENCY 0x00000002
15177 #define MA_PA_SINK_HARDWARE 0x00000004
15178 #define MA_PA_SINK_NETWORK 0x00000008
15179 #define MA_PA_SINK_HW_MUTE_CTRL 0x00000010
15180 #define MA_PA_SINK_DECIBEL_VOLUME 0x00000020
15181 #define MA_PA_SINK_FLAT_VOLUME 0x00000040
15182 #define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080
15183 #define MA_PA_SINK_SET_FORMATS 0x00000100
15184 
15185 typedef int ma_pa_source_flags_t;
15186 #define MA_PA_SOURCE_NOFLAGS 0x00000000
15187 #define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001
15188 #define MA_PA_SOURCE_LATENCY 0x00000002
15189 #define MA_PA_SOURCE_HARDWARE 0x00000004
15190 #define MA_PA_SOURCE_NETWORK 0x00000008
15191 #define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010
15192 #define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020
15193 #define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040
15194 #define MA_PA_SOURCE_FLAT_VOLUME 0x00000080
15195 
15196 typedef int ma_pa_context_state_t;
15197 #define MA_PA_CONTEXT_UNCONNECTED 0
15198 #define MA_PA_CONTEXT_CONNECTING 1
15199 #define MA_PA_CONTEXT_AUTHORIZING 2
15200 #define MA_PA_CONTEXT_SETTING_NAME 3
15201 #define MA_PA_CONTEXT_READY 4
15202 #define MA_PA_CONTEXT_FAILED 5
15203 #define MA_PA_CONTEXT_TERMINATED 6
15204 
15205 typedef int ma_pa_stream_state_t;
15206 #define MA_PA_STREAM_UNCONNECTED 0
15207 #define MA_PA_STREAM_CREATING 1
15208 #define MA_PA_STREAM_READY 2
15209 #define MA_PA_STREAM_FAILED 3
15210 #define MA_PA_STREAM_TERMINATED 4
15211 
15212 typedef int ma_pa_operation_state_t;
15213 #define MA_PA_OPERATION_RUNNING 0
15214 #define MA_PA_OPERATION_DONE 1
15215 #define MA_PA_OPERATION_CANCELLED 2
15216 
15217 typedef int ma_pa_sink_state_t;
15218 #define MA_PA_SINK_INVALID_STATE -1
15219 #define MA_PA_SINK_RUNNING 0
15220 #define MA_PA_SINK_IDLE 1
15221 #define MA_PA_SINK_SUSPENDED 2
15222 
15223 typedef int ma_pa_source_state_t;
15224 #define MA_PA_SOURCE_INVALID_STATE -1
15225 #define MA_PA_SOURCE_RUNNING 0
15226 #define MA_PA_SOURCE_IDLE 1
15227 #define MA_PA_SOURCE_SUSPENDED 2
15228 
15229 typedef int ma_pa_seek_mode_t;
15230 #define MA_PA_SEEK_RELATIVE 0
15231 #define MA_PA_SEEK_ABSOLUTE 1
15232 #define MA_PA_SEEK_RELATIVE_ON_READ 2
15233 #define MA_PA_SEEK_RELATIVE_END 3
15234 
15235 typedef int ma_pa_channel_position_t;
15236 #define MA_PA_CHANNEL_POSITION_INVALID -1
15237 #define MA_PA_CHANNEL_POSITION_MONO 0
15238 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1
15239 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2
15240 #define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3
15241 #define MA_PA_CHANNEL_POSITION_REAR_CENTER 4
15242 #define MA_PA_CHANNEL_POSITION_REAR_LEFT 5
15243 #define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6
15244 #define MA_PA_CHANNEL_POSITION_LFE 7
15245 #define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8
15246 #define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9
15247 #define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10
15248 #define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11
15249 #define MA_PA_CHANNEL_POSITION_AUX0 12
15250 #define MA_PA_CHANNEL_POSITION_AUX1 13
15251 #define MA_PA_CHANNEL_POSITION_AUX2 14
15252 #define MA_PA_CHANNEL_POSITION_AUX3 15
15253 #define MA_PA_CHANNEL_POSITION_AUX4 16
15254 #define MA_PA_CHANNEL_POSITION_AUX5 17
15255 #define MA_PA_CHANNEL_POSITION_AUX6 18
15256 #define MA_PA_CHANNEL_POSITION_AUX7 19
15257 #define MA_PA_CHANNEL_POSITION_AUX8 20
15258 #define MA_PA_CHANNEL_POSITION_AUX9 21
15259 #define MA_PA_CHANNEL_POSITION_AUX10 22
15260 #define MA_PA_CHANNEL_POSITION_AUX11 23
15261 #define MA_PA_CHANNEL_POSITION_AUX12 24
15262 #define MA_PA_CHANNEL_POSITION_AUX13 25
15263 #define MA_PA_CHANNEL_POSITION_AUX14 26
15264 #define MA_PA_CHANNEL_POSITION_AUX15 27
15265 #define MA_PA_CHANNEL_POSITION_AUX16 28
15266 #define MA_PA_CHANNEL_POSITION_AUX17 29
15267 #define MA_PA_CHANNEL_POSITION_AUX18 30
15268 #define MA_PA_CHANNEL_POSITION_AUX19 31
15269 #define MA_PA_CHANNEL_POSITION_AUX20 32
15270 #define MA_PA_CHANNEL_POSITION_AUX21 33
15271 #define MA_PA_CHANNEL_POSITION_AUX22 34
15272 #define MA_PA_CHANNEL_POSITION_AUX23 35
15273 #define MA_PA_CHANNEL_POSITION_AUX24 36
15274 #define MA_PA_CHANNEL_POSITION_AUX25 37
15275 #define MA_PA_CHANNEL_POSITION_AUX26 38
15276 #define MA_PA_CHANNEL_POSITION_AUX27 39
15277 #define MA_PA_CHANNEL_POSITION_AUX28 40
15278 #define MA_PA_CHANNEL_POSITION_AUX29 41
15279 #define MA_PA_CHANNEL_POSITION_AUX30 42
15280 #define MA_PA_CHANNEL_POSITION_AUX31 43
15281 #define MA_PA_CHANNEL_POSITION_TOP_CENTER 44
15282 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45
15283 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46
15284 #define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47
15285 #define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48
15286 #define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49
15287 #define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50
15288 #define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT
15289 #define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT
15290 #define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER
15291 #define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE
15292 
15293 typedef int ma_pa_channel_map_def_t;
15294 #define MA_PA_CHANNEL_MAP_AIFF 0
15295 #define MA_PA_CHANNEL_MAP_ALSA 1
15296 #define MA_PA_CHANNEL_MAP_AUX 2
15297 #define MA_PA_CHANNEL_MAP_WAVEEX 3
15298 #define MA_PA_CHANNEL_MAP_OSS 4
15299 #define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF
15300 
15301 typedef int ma_pa_sample_format_t;
15302 #define MA_PA_SAMPLE_INVALID -1
15303 #define MA_PA_SAMPLE_U8 0
15304 #define MA_PA_SAMPLE_ALAW 1
15305 #define MA_PA_SAMPLE_ULAW 2
15306 #define MA_PA_SAMPLE_S16LE 3
15307 #define MA_PA_SAMPLE_S16BE 4
15308 #define MA_PA_SAMPLE_FLOAT32LE 5
15309 #define MA_PA_SAMPLE_FLOAT32BE 6
15310 #define MA_PA_SAMPLE_S32LE 7
15311 #define MA_PA_SAMPLE_S32BE 8
15312 #define MA_PA_SAMPLE_S24LE 9
15313 #define MA_PA_SAMPLE_S24BE 10
15314 #define MA_PA_SAMPLE_S24_32LE 11
15315 #define MA_PA_SAMPLE_S24_32BE 12
15316 
15317 typedef struct ma_pa_mainloop ma_pa_mainloop;
15318 typedef struct ma_pa_mainloop_api ma_pa_mainloop_api;
15319 typedef struct ma_pa_context ma_pa_context;
15320 typedef struct ma_pa_operation ma_pa_operation;
15321 typedef struct ma_pa_stream ma_pa_stream;
15322 typedef struct ma_pa_spawn_api ma_pa_spawn_api;
15323 
15324 typedef struct
15325 {
15326  ma_uint32 maxlength;
15327  ma_uint32 tlength;
15328  ma_uint32 prebuf;
15329  ma_uint32 minreq;
15330  ma_uint32 fragsize;
15331 } ma_pa_buffer_attr;
15332 
15333 typedef struct
15334 {
15335  ma_uint8 channels;
15336  ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX];
15337 } ma_pa_channel_map;
15338 
15339 typedef struct
15340 {
15341  ma_uint8 channels;
15342  ma_uint32 values[MA_PA_CHANNELS_MAX];
15343 } ma_pa_cvolume;
15344 
15345 typedef struct
15346 {
15347  ma_pa_sample_format_t format;
15348  ma_uint32 rate;
15349  ma_uint8 channels;
15350 } ma_pa_sample_spec;
15351 
15352 typedef struct
15353 {
15354  const char* name;
15355  ma_uint32 index;
15356  const char* description;
15357  ma_pa_sample_spec sample_spec;
15358  ma_pa_channel_map channel_map;
15359  ma_uint32 owner_module;
15360  ma_pa_cvolume volume;
15361  int mute;
15362  ma_uint32 monitor_source;
15363  const char* monitor_source_name;
15364  ma_uint64 latency;
15365  const char* driver;
15366  ma_pa_sink_flags_t flags;
15367  void* proplist;
15368  ma_uint64 configured_latency;
15369  ma_uint32 base_volume;
15370  ma_pa_sink_state_t state;
15371  ma_uint32 n_volume_steps;
15372  ma_uint32 card;
15373  ma_uint32 n_ports;
15374  void** ports;
15375  void* active_port;
15376  ma_uint8 n_formats;
15377  void** formats;
15378 } ma_pa_sink_info;
15379 
15380 typedef struct
15381 {
15382  const char *name;
15383  ma_uint32 index;
15384  const char *description;
15385  ma_pa_sample_spec sample_spec;
15386  ma_pa_channel_map channel_map;
15387  ma_uint32 owner_module;
15388  ma_pa_cvolume volume;
15389  int mute;
15390  ma_uint32 monitor_of_sink;
15391  const char *monitor_of_sink_name;
15392  ma_uint64 latency;
15393  const char *driver;
15394  ma_pa_source_flags_t flags;
15395  void* proplist;
15396  ma_uint64 configured_latency;
15397  ma_uint32 base_volume;
15398  ma_pa_source_state_t state;
15399  ma_uint32 n_volume_steps;
15400  ma_uint32 card;
15401  ma_uint32 n_ports;
15402  void** ports;
15403  void* active_port;
15404  ma_uint8 n_formats;
15405  void** formats;
15406 } ma_pa_source_info;
15407 
15408 typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata);
15409 typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata);
15410 typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata);
15411 typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata);
15412 typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata);
15413 typedef void (* ma_pa_free_cb_t) (void* p);
15414 #endif
15415 
15416 
15417 typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) ();
15418 typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m);
15419 typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m);
15420 typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval);
15421 typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m);
15422 typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name);
15423 typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c);
15424 typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api);
15425 typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c);
15426 typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata);
15427 typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (ma_pa_context* c);
15428 typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata);
15429 typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata);
15430 typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata);
15431 typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata);
15432 typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o);
15433 typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (ma_pa_operation* o);
15434 typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def);
15435 typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m);
15436 typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss);
15437 typedef ma_pa_stream* (* ma_pa_stream_new_proc) (ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map);
15438 typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s);
15439 typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream);
15440 typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags);
15441 typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s);
15442 typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (ma_pa_stream* s);
15443 typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s);
15444 typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s);
15445 typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s);
15446 typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata);
15447 typedef const char* (* ma_pa_stream_get_device_name_proc) (ma_pa_stream* s);
15448 typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
15449 typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
15450 typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
15451 typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
15452 typedef int (* ma_pa_stream_is_corked_proc) (ma_pa_stream* s);
15453 typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata);
15454 typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
15455 typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes);
15456 typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek);
15457 typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes);
15458 typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s);
15459 typedef size_t (* ma_pa_stream_writable_size_proc) (ma_pa_stream* s);
15460 typedef size_t (* ma_pa_stream_readable_size_proc) (ma_pa_stream* s);
15461 
15462 typedef struct
15463 {
15464  ma_uint32 count;
15465  ma_uint32 capacity;
15466  ma_device_info* pInfo;
15467 } ma_pulse_device_enum_data;
15468 
15469 ma_result ma_result_from_pulse(int result)
15470 {
15471  switch (result) {
15472  case MA_PA_OK: return MA_SUCCESS;
15473  case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED;
15474  case MA_PA_ERR_INVALID: return MA_INVALID_ARGS;
15475  case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE;
15476  default: return MA_ERROR;
15477  }
15478 }
15479 
15480 #if 0
15481 ma_pa_sample_format_t ma_format_to_pulse(ma_format format)
15482 {
15483  if (ma_is_little_endian()) {
15484  switch (format) {
15485  case ma_format_s16: return MA_PA_SAMPLE_S16LE;
15486  case ma_format_s24: return MA_PA_SAMPLE_S24LE;
15487  case ma_format_s32: return MA_PA_SAMPLE_S32LE;
15488  case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE;
15489  default: break;
15490  }
15491  } else {
15492  switch (format) {
15493  case ma_format_s16: return MA_PA_SAMPLE_S16BE;
15494  case ma_format_s24: return MA_PA_SAMPLE_S24BE;
15495  case ma_format_s32: return MA_PA_SAMPLE_S32BE;
15496  case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE;
15497  default: break;
15498  }
15499  }
15500 
15501  /* Endian agnostic. */
15502  switch (format) {
15503  case ma_format_u8: return MA_PA_SAMPLE_U8;
15504  default: return MA_PA_SAMPLE_INVALID;
15505  }
15506 }
15507 #endif
15508 
15509 ma_format ma_format_from_pulse(ma_pa_sample_format_t format)
15510 {
15511  if (ma_is_little_endian()) {
15512  switch (format) {
15513  case MA_PA_SAMPLE_S16LE: return ma_format_s16;
15514  case MA_PA_SAMPLE_S24LE: return ma_format_s24;
15515  case MA_PA_SAMPLE_S32LE: return ma_format_s32;
15516  case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32;
15517  default: break;
15518  }
15519  } else {
15520  switch (format) {
15521  case MA_PA_SAMPLE_S16BE: return ma_format_s16;
15522  case MA_PA_SAMPLE_S24BE: return ma_format_s24;
15523  case MA_PA_SAMPLE_S32BE: return ma_format_s32;
15524  case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32;
15525  default: break;
15526  }
15527  }
15528 
15529  /* Endian agnostic. */
15530  switch (format) {
15531  case MA_PA_SAMPLE_U8: return ma_format_u8;
15532  default: return ma_format_unknown;
15533  }
15534 }
15535 
15536 ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position)
15537 {
15538  switch (position)
15539  {
15540  case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE;
15541  case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO;
15542  case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
15543  case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
15544  case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
15545  case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER;
15546  case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT;
15547  case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT;
15548  case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE;
15549  case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
15550  case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
15551  case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
15552  case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
15553  case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0;
15554  case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1;
15555  case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2;
15556  case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3;
15557  case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4;
15558  case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5;
15559  case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6;
15560  case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7;
15561  case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8;
15562  case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9;
15563  case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10;
15564  case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11;
15565  case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12;
15566  case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13;
15567  case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14;
15568  case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15;
15569  case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16;
15570  case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17;
15571  case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18;
15572  case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19;
15573  case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20;
15574  case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21;
15575  case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22;
15576  case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23;
15577  case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24;
15578  case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25;
15579  case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26;
15580  case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27;
15581  case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28;
15582  case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29;
15583  case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30;
15584  case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31;
15585  case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
15586  case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
15587  case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
15588  case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
15589  case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
15590  case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
15591  case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
15592  default: return MA_CHANNEL_NONE;
15593  }
15594 }
15595 
15596 #if 0
15597 ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position)
15598 {
15599  switch (position)
15600  {
15601  case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID;
15602  case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT;
15603  case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT;
15604  case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER;
15605  case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE;
15606  case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT;
15607  case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT;
15608  case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
15609  case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
15610  case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER;
15611  case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT;
15612  case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT;
15613  case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER;
15614  case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
15615  case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
15616  case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
15617  case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT;
15618  case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER;
15619  case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
15620  case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18;
15621  case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19;
15622  case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20;
15623  case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21;
15624  case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22;
15625  case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23;
15626  case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24;
15627  case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25;
15628  case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26;
15629  case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27;
15630  case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28;
15631  case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29;
15632  case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30;
15633  case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31;
15634  default: return (ma_pa_channel_position_t)position;
15635  }
15636 }
15637 #endif
15638 
15639 ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_pa_mainloop* pMainLoop, ma_pa_operation* pOP)
15640 {
15641  ma_assert(pContext != NULL);
15642  ma_assert(pMainLoop != NULL);
15643  ma_assert(pOP != NULL);
15644 
15645  while (((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP) == MA_PA_OPERATION_RUNNING) {
15646  int error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL);
15647  if (error < 0) {
15648  return ma_result_from_pulse(error);
15649  }
15650  }
15651 
15652  return MA_SUCCESS;
15653 }
15654 
15655 ma_result ma_device__wait_for_operation__pulse(ma_device* pDevice, ma_pa_operation* pOP)
15656 {
15657  ma_assert(pDevice != NULL);
15658  ma_assert(pOP != NULL);
15659 
15660  return ma_wait_for_operation__pulse(pDevice->pContext, (ma_pa_mainloop*)pDevice->pulse.pMainLoop, pOP);
15661 }
15662 
15663 
15664 ma_bool32 ma_context_is_device_id_equal__pulse(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
15665 {
15666  ma_assert(pContext != NULL);
15667  ma_assert(pID0 != NULL);
15668  ma_assert(pID1 != NULL);
15669  (void)pContext;
15670 
15671  return ma_strcmp(pID0->pulse, pID1->pulse) == 0;
15672 }
15673 
15674 
15675 typedef struct
15676 {
15677  ma_context* pContext;
15679  void* pUserData;
15680  ma_bool32 isTerminated;
15681 } ma_context_enumerate_devices_callback_data__pulse;
15682 
15683 void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData)
15684 {
15685  ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
15686  ma_device_info deviceInfo;
15687 
15688  ma_assert(pData != NULL);
15689 
15690  if (endOfList || pData->isTerminated) {
15691  return;
15692  }
15693 
15694  ma_zero_object(&deviceInfo);
15695 
15696  /* The name from PulseAudio is the ID for miniaudio. */
15697  if (pSinkInfo->name != NULL) {
15698  ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);
15699  }
15700 
15701  /* The description from PulseAudio is the name for miniaudio. */
15702  if (pSinkInfo->description != NULL) {
15703  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);
15704  }
15705 
15706  pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData);
15707 
15708  (void)pPulseContext; /* Unused. */
15709 }
15710 
15711 void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSinkInfo, int endOfList, void* pUserData)
15712 {
15713  ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
15714  ma_device_info deviceInfo;
15715 
15716  ma_assert(pData != NULL);
15717 
15718  if (endOfList || pData->isTerminated) {
15719  return;
15720  }
15721 
15722  ma_zero_object(&deviceInfo);
15723 
15724  /* The name from PulseAudio is the ID for miniaudio. */
15725  if (pSinkInfo->name != NULL) {
15726  ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);
15727  }
15728 
15729  /* The description from PulseAudio is the name for miniaudio. */
15730  if (pSinkInfo->description != NULL) {
15731  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);
15732  }
15733 
15734  pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData);
15735 
15736  (void)pPulseContext; /* Unused. */
15737 }
15738 
15739 ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
15740 {
15742  ma_context_enumerate_devices_callback_data__pulse callbackData;
15743  ma_pa_operation* pOP = NULL;
15744  ma_pa_mainloop* pMainLoop;
15745  ma_pa_mainloop_api* pAPI;
15746  ma_pa_context* pPulseContext;
15747  int error;
15748 
15749  ma_assert(pContext != NULL);
15750  ma_assert(callback != NULL);
15751 
15752  callbackData.pContext = pContext;
15753  callbackData.callback = callback;
15754  callbackData.pUserData = pUserData;
15755  callbackData.isTerminated = MA_FALSE;
15756 
15757  pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
15758  if (pMainLoop == NULL) {
15760  }
15761 
15762  pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop);
15763  if (pAPI == NULL) {
15764  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
15766  }
15767 
15768  pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->pulse.pApplicationName);
15769  if (pPulseContext == NULL) {
15770  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
15772  }
15773 
15774  error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->pulse.pServerName, (pContext->pulse.tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL);
15775  if (error != MA_PA_OK) {
15776  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
15777  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
15778  return ma_result_from_pulse(error);
15779  }
15780 
15781  for (;;) {
15782  ma_pa_context_state_t state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
15783  if (state == MA_PA_CONTEXT_READY) {
15784  break; /* Success. */
15785  }
15786  if (state == MA_PA_CONTEXT_CONNECTING || state == MA_PA_CONTEXT_AUTHORIZING || state == MA_PA_CONTEXT_SETTING_NAME) {
15787  error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL);
15788  if (error < 0) {
15789  result = ma_result_from_pulse(error);
15790  goto done;
15791  }
15792 
15793 #ifdef MA_DEBUG_OUTPUT
15794  printf("[PulseAudio] pa_context_get_state() returned %d. Waiting.\n", state);
15795 #endif
15796  continue; /* Keep trying. */
15797  }
15798  if (state == MA_PA_CONTEXT_UNCONNECTED || state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
15799 #ifdef MA_DEBUG_OUTPUT
15800  printf("[PulseAudio] pa_context_get_state() returned %d. Failed.\n", state);
15801 #endif
15802  goto done; /* Failed. */
15803  }
15804  }
15805 
15806 
15807  /* Playback. */
15808  if (!callbackData.isTerminated) {
15809  pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)(pPulseContext, ma_context_enumerate_devices_sink_callback__pulse, &callbackData);
15810  if (pOP == NULL) {
15811  result = MA_ERROR;
15812  goto done;
15813  }
15814 
15815  result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
15816  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
15817  if (result != MA_SUCCESS) {
15818  goto done;
15819  }
15820  }
15821 
15822 
15823  /* Capture. */
15824  if (!callbackData.isTerminated) {
15825  pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)(pPulseContext, ma_context_enumerate_devices_source_callback__pulse, &callbackData);
15826  if (pOP == NULL) {
15827  result = MA_ERROR;
15828  goto done;
15829  }
15830 
15831  result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
15832  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
15833  if (result != MA_SUCCESS) {
15834  goto done;
15835  }
15836  }
15837 
15838 done:
15839  ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext);
15840  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
15841  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
15842  return result;
15843 }
15844 
15845 
15846 typedef struct
15847 {
15848  ma_device_info* pDeviceInfo;
15849  ma_bool32 foundDevice;
15850 } ma_context_get_device_info_callback_data__pulse;
15851 
15852 void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
15853 {
15854  ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
15855 
15856  if (endOfList > 0) {
15857  return;
15858  }
15859 
15860  ma_assert(pData != NULL);
15861  pData->foundDevice = MA_TRUE;
15862 
15863  if (pInfo->name != NULL) {
15864  ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
15865  }
15866 
15867  if (pInfo->description != NULL) {
15868  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
15869  }
15870 
15871  pData->pDeviceInfo->minChannels = pInfo->sample_spec.channels;
15872  pData->pDeviceInfo->maxChannels = pInfo->sample_spec.channels;
15873  pData->pDeviceInfo->minSampleRate = pInfo->sample_spec.rate;
15874  pData->pDeviceInfo->maxSampleRate = pInfo->sample_spec.rate;
15875  pData->pDeviceInfo->formatCount = 1;
15876  pData->pDeviceInfo->formats[0] = ma_format_from_pulse(pInfo->sample_spec.format);
15877 
15878  (void)pPulseContext; /* Unused. */
15879 }
15880 
15881 void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
15882 {
15883  ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
15884 
15885  if (endOfList > 0) {
15886  return;
15887  }
15888 
15889  ma_assert(pData != NULL);
15890  pData->foundDevice = MA_TRUE;
15891 
15892  if (pInfo->name != NULL) {
15893  ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
15894  }
15895 
15896  if (pInfo->description != NULL) {
15897  ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
15898  }
15899 
15900  pData->pDeviceInfo->minChannels = pInfo->sample_spec.channels;
15901  pData->pDeviceInfo->maxChannels = pInfo->sample_spec.channels;
15902  pData->pDeviceInfo->minSampleRate = pInfo->sample_spec.rate;
15903  pData->pDeviceInfo->maxSampleRate = pInfo->sample_spec.rate;
15904  pData->pDeviceInfo->formatCount = 1;
15905  pData->pDeviceInfo->formats[0] = ma_format_from_pulse(pInfo->sample_spec.format);
15906 
15907  (void)pPulseContext; /* Unused. */
15908 }
15909 
15910 ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
15911 {
15913  ma_context_get_device_info_callback_data__pulse callbackData;
15914  ma_pa_operation* pOP = NULL;
15915  ma_pa_mainloop* pMainLoop;
15916  ma_pa_mainloop_api* pAPI;
15917  ma_pa_context* pPulseContext;
15918  int error;
15919 
15920  ma_assert(pContext != NULL);
15921 
15922  /* No exclusive mode with the PulseAudio backend. */
15923  if (shareMode == ma_share_mode_exclusive) {
15925  }
15926 
15927  callbackData.pDeviceInfo = pDeviceInfo;
15928  callbackData.foundDevice = MA_FALSE;
15929 
15930  pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
15931  if (pMainLoop == NULL) {
15933  }
15934 
15935  pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop);
15936  if (pAPI == NULL) {
15937  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
15939  }
15940 
15941  pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->pulse.pApplicationName);
15942  if (pPulseContext == NULL) {
15943  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
15945  }
15946 
15947  error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->pulse.pServerName, 0, NULL);
15948  if (error != MA_PA_OK) {
15949  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
15950  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
15951  return ma_result_from_pulse(error);
15952  }
15953 
15954  for (;;) {
15955  ma_pa_context_state_t state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
15956  if (state == MA_PA_CONTEXT_READY) {
15957  break; /* Success. */
15958  }
15959  if (state == MA_PA_CONTEXT_CONNECTING || state == MA_PA_CONTEXT_AUTHORIZING || state == MA_PA_CONTEXT_SETTING_NAME) {
15960  error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)(pMainLoop, 1, NULL);
15961  if (error < 0) {
15962  result = ma_result_from_pulse(error);
15963  goto done;
15964  }
15965 
15966 #ifdef MA_DEBUG_OUTPUT
15967  printf("[PulseAudio] pa_context_get_state() returned %d. Waiting.\n", state);
15968 #endif
15969  continue; /* Keep trying. */
15970  }
15971  if (state == MA_PA_CONTEXT_UNCONNECTED || state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
15972 #ifdef MA_DEBUG_OUTPUT
15973  printf("[PulseAudio] pa_context_get_state() returned %d. Failed.\n", state);
15974 #endif
15975  goto done; /* Failed. */
15976  }
15977  }
15978 
15979  if (deviceType == ma_device_type_playback) {
15980  pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)(pPulseContext, pDeviceID->pulse, ma_context_get_device_info_sink_callback__pulse, &callbackData);
15981  } else {
15982  pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)(pPulseContext, pDeviceID->pulse, ma_context_get_device_info_source_callback__pulse, &callbackData);
15983  }
15984 
15985  if (pOP != NULL) {
15986  ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
15987  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
15988  } else {
15989  result = MA_ERROR;
15990  goto done;
15991  }
15992 
15993  if (!callbackData.foundDevice) {
15994  result = MA_NO_DEVICE;
15995  goto done;
15996  }
15997 
15998 
15999 done:
16000  ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext);
16001  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
16002  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
16003  return result;
16004 }
16005 
16006 
16007 void ma_pulse_device_state_callback(ma_pa_context* pPulseContext, void* pUserData)
16008 {
16009  ma_device* pDevice;
16010  ma_context* pContext;
16011 
16012  pDevice = (ma_device*)pUserData;
16013  ma_assert(pDevice != NULL);
16014 
16015  pContext = pDevice->pContext;
16016  ma_assert(pContext != NULL);
16017 
16018  pDevice->pulse.pulseContextState = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)(pPulseContext);
16019 }
16020 
16021 void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
16022 {
16023  ma_pa_sink_info* pInfoOut;
16024 
16025  if (endOfList > 0) {
16026  return;
16027  }
16028 
16029  pInfoOut = (ma_pa_sink_info*)pUserData;
16030  ma_assert(pInfoOut != NULL);
16031 
16032  *pInfoOut = *pInfo;
16033 
16034  (void)pPulseContext; /* Unused. */
16035 }
16036 
16037 void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
16038 {
16039  ma_pa_source_info* pInfoOut;
16040 
16041  if (endOfList > 0) {
16042  return;
16043  }
16044 
16045  pInfoOut = (ma_pa_source_info*)pUserData;
16046  ma_assert(pInfoOut != NULL);
16047 
16048  *pInfoOut = *pInfo;
16049 
16050  (void)pPulseContext; /* Unused. */
16051 }
16052 
16053 void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
16054 {
16055  ma_device* pDevice;
16056 
16057  if (endOfList > 0) {
16058  return;
16059  }
16060 
16061  pDevice = (ma_device*)pUserData;
16062  ma_assert(pDevice != NULL);
16063 
16064  ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1);
16065 
16066  (void)pPulseContext; /* Unused. */
16067 }
16068 
16069 void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
16070 {
16071  ma_device* pDevice;
16072 
16073  if (endOfList > 0) {
16074  return;
16075  }
16076 
16077  pDevice = (ma_device*)pUserData;
16078  ma_assert(pDevice != NULL);
16079 
16080  ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1);
16081 
16082  (void)pPulseContext; /* Unused. */
16083 }
16084 
16085 void ma_device_uninit__pulse(ma_device* pDevice)
16086 {
16087  ma_context* pContext;
16088 
16089  ma_assert(pDevice != NULL);
16090 
16091  pContext = pDevice->pContext;
16092  ma_assert(pContext != NULL);
16093 
16094  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
16095  ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
16096  ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
16097  }
16098  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
16099  ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
16100  ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
16101  }
16102 
16103  ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
16104  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
16105  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
16106 }
16107 
16108 ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 bufferSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss)
16109 {
16110  ma_pa_buffer_attr attr;
16111  attr.maxlength = bufferSizeInFrames * ma_get_bytes_per_sample(ma_format_from_pulse(ss->format)) * ss->channels;
16112  attr.tlength = attr.maxlength / periods;
16113  attr.prebuf = (ma_uint32)-1;
16114  attr.minreq = (ma_uint32)-1;
16115  attr.fragsize = attr.maxlength / periods;
16116 
16117  return attr;
16118 }
16119 
16120 ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap)
16121 {
16122  static int g_StreamCounter = 0;
16123  char actualStreamName[256];
16124 
16125  if (pStreamName != NULL) {
16126  ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1);
16127  } else {
16128  ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:");
16129  ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */
16130  }
16131  g_StreamCounter += 1;
16132 
16133  return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap);
16134 }
16135 
16136 ma_result ma_device_init__pulse(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
16137 {
16139  int error = 0;
16140  const char* devPlayback = NULL;
16141  const char* devCapture = NULL;
16142  ma_uint32 bufferSizeInMilliseconds;
16143  ma_pa_sink_info sinkInfo;
16144  ma_pa_source_info sourceInfo;
16145  ma_pa_operation* pOP = NULL;
16146  ma_pa_sample_spec ss;
16147  ma_pa_channel_map cmap;
16148  ma_pa_buffer_attr attr;
16149  const ma_pa_sample_spec* pActualSS = NULL;
16150  const ma_pa_channel_map* pActualCMap = NULL;
16151  const ma_pa_buffer_attr* pActualAttr = NULL;
16152  ma_uint32 iChannel;
16153  ma_pa_stream_flags_t streamFlags;
16154 
16155  ma_assert(pDevice != NULL);
16156  ma_zero_object(&pDevice->pulse);
16157 
16158  if (pConfig->deviceType == ma_device_type_loopback) {
16160  }
16161 
16162  /* No exclusive mode with the PulseAudio backend. */
16166  }
16167 
16168  if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID != NULL) {
16169  devPlayback = pConfig->playback.pDeviceID->pulse;
16170  }
16171  if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID != NULL) {
16172  devCapture = pConfig->capture.pDeviceID->pulse;
16173  }
16174 
16175  bufferSizeInMilliseconds = pConfig->bufferSizeInMilliseconds;
16176  if (bufferSizeInMilliseconds == 0) {
16177  bufferSizeInMilliseconds = ma_calculate_buffer_size_in_milliseconds_from_frames(pConfig->bufferSizeInFrames, pConfig->sampleRate);
16178  }
16179 
16180  pDevice->pulse.pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
16181  if (pDevice->pulse.pMainLoop == NULL) {
16182  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create main loop for device.", MA_FAILED_TO_INIT_BACKEND);
16183  goto on_error0;
16184  }
16185 
16186  pDevice->pulse.pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
16187  if (pDevice->pulse.pAPI == NULL) {
16188  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve PulseAudio main loop.", MA_FAILED_TO_INIT_BACKEND);
16189  goto on_error1;
16190  }
16191 
16192  pDevice->pulse.pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)((ma_pa_mainloop_api*)pDevice->pulse.pAPI, pContext->pulse.pApplicationName);
16193  if (pDevice->pulse.pPulseContext == NULL) {
16194  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context for device.", MA_FAILED_TO_INIT_BACKEND);
16195  goto on_error1;
16196  }
16197 
16198  error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pDevice->pulse.pPulseContext, pContext->pulse.pServerName, (pContext->pulse.tryAutoSpawn) ? 0 : MA_PA_CONTEXT_NOAUTOSPAWN, NULL);
16199  if (error != MA_PA_OK) {
16200  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.", ma_result_from_pulse(error));
16201  goto on_error2;
16202  }
16203 
16204 
16205  pDevice->pulse.pulseContextState = MA_PA_CONTEXT_UNCONNECTED;
16206  ((ma_pa_context_set_state_callback_proc)pContext->pulse.pa_context_set_state_callback)((ma_pa_context*)pDevice->pulse.pPulseContext, ma_pulse_device_state_callback, pDevice);
16207 
16208  /* Wait for PulseAudio to get itself ready before returning. */
16209  for (;;) {
16210  if (pDevice->pulse.pulseContextState == MA_PA_CONTEXT_READY) {
16211  break;
16212  }
16213 
16214  /* An error may have occurred. */
16215  if (pDevice->pulse.pulseContextState == MA_PA_CONTEXT_FAILED || pDevice->pulse.pulseContextState == MA_PA_CONTEXT_TERMINATED) {
16216  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context.", MA_ERROR);
16217  goto on_error3;
16218  }
16219 
16220  error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
16221  if (error < 0) {
16222  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] The PulseAudio main loop returned an error while connecting the PulseAudio context.", ma_result_from_pulse(error));
16223  goto on_error3;
16224  }
16225  }
16226 
16227  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
16228  pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devCapture, ma_device_source_info_callback, &sourceInfo);
16229  if (pOP != NULL) {
16230  ma_device__wait_for_operation__pulse(pDevice, pOP);
16231  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
16232  } else {
16233  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device.", ma_result_from_pulse(error));
16234  goto on_error3;
16235  }
16236 
16237  ss = sourceInfo.sample_spec;
16238  cmap = sourceInfo.channel_map;
16239 
16240  pDevice->capture.internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(bufferSizeInMilliseconds, ss.rate);
16241  pDevice->capture.internalPeriods = pConfig->periods;
16242 
16243  attr = ma_device__pa_buffer_attr_new(pDevice->capture.internalBufferSizeInFrames, pConfig->periods, &ss);
16244  #ifdef MA_DEBUG_OUTPUT
16245  printf("[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalBufferSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->capture.internalBufferSizeInFrames);
16246  #endif
16247 
16248  pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap);
16249  if (pDevice->pulse.pStreamCapture == NULL) {
16250  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
16251  goto on_error3;
16252  }
16253 
16254  streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
16255  if (devCapture != NULL) {
16256  streamFlags |= MA_PA_STREAM_DONT_MOVE;
16257  }
16258 
16259  error = ((ma_pa_stream_connect_record_proc)pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, streamFlags);
16260  if (error != MA_PA_OK) {
16261  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream.", ma_result_from_pulse(error));
16262  goto on_error4;
16263  }
16264 
16265  while (((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pDevice->pulse.pStreamCapture) != MA_PA_STREAM_READY) {
16266  error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
16267  if (error < 0) {
16268  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] The PulseAudio main loop returned an error while connecting the PulseAudio capture stream.", ma_result_from_pulse(error));
16269  goto on_error5;
16270  }
16271  }
16272 
16273  /* Internal format. */
16274  pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
16275  if (pActualSS != NULL) {
16276  /* If anything has changed between the requested and the actual sample spec, we need to update the buffer. */
16277  if (ss.format != pActualSS->format || ss.channels != pActualSS->channels || ss.rate != pActualSS->rate) {
16278  attr = ma_device__pa_buffer_attr_new(pDevice->capture.internalBufferSizeInFrames, pConfig->periods, pActualSS);
16279 
16280  pOP = ((ma_pa_stream_set_buffer_attr_proc)pContext->pulse.pa_stream_set_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture, &attr, NULL, NULL);
16281  if (pOP != NULL) {
16282  ma_device__wait_for_operation__pulse(pDevice, pOP);
16283  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
16284  }
16285  }
16286 
16287  ss = *pActualSS;
16288  }
16289 
16290  pDevice->capture.internalFormat = ma_format_from_pulse(ss.format);
16291  pDevice->capture.internalChannels = ss.channels;
16292  pDevice->capture.internalSampleRate = ss.rate;
16293 
16294  /* Internal channel map. */
16295  pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
16296  if (pActualCMap != NULL) {
16297  cmap = *pActualCMap;
16298  }
16299  for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
16300  pDevice->capture.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
16301  }
16302 
16303  /* Buffer. */
16304  pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
16305  if (pActualAttr != NULL) {
16306  attr = *pActualAttr;
16307  }
16308  pDevice->capture.internalBufferSizeInFrames = attr.maxlength / (ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pDevice->capture.internalChannels);
16309  pDevice->capture.internalPeriods = attr.maxlength / attr.fragsize;
16310  #ifdef MA_DEBUG_OUTPUT
16311  printf("[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalBufferSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->capture.internalBufferSizeInFrames);
16312  #endif
16313 
16314  /* Name. */
16315  devCapture = ((ma_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
16316  if (devCapture != NULL) {
16317  ma_pa_operation* pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devCapture, ma_device_source_name_callback, pDevice);
16318  if (pOP != NULL) {
16319  ma_device__wait_for_operation__pulse(pDevice, pOP);
16320  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
16321  }
16322  }
16323  }
16324 
16325  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
16326  pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devPlayback, ma_device_sink_info_callback, &sinkInfo);
16327  if (pOP != NULL) {
16328  ma_device__wait_for_operation__pulse(pDevice, pOP);
16329  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
16330  } else {
16331  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.", ma_result_from_pulse(error));
16332  goto on_error3;
16333  }
16334 
16335  ss = sinkInfo.sample_spec;
16336  cmap = sinkInfo.channel_map;
16337 
16338  pDevice->playback.internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(bufferSizeInMilliseconds, ss.rate);
16339  pDevice->playback.internalPeriods = pConfig->periods;
16340 
16341  attr = ma_device__pa_buffer_attr_new(pDevice->playback.internalBufferSizeInFrames, pConfig->periods, &ss);
16342  #ifdef MA_DEBUG_OUTPUT
16343  printf("[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalBufferSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->playback.internalBufferSizeInFrames);
16344  #endif
16345 
16346  pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap);
16347  if (pDevice->pulse.pStreamPlayback == NULL) {
16348  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
16349  goto on_error3;
16350  }
16351 
16352  streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS;
16353  if (devPlayback != NULL) {
16354  streamFlags |= MA_PA_STREAM_DONT_MOVE;
16355  }
16356 
16357  error = ((ma_pa_stream_connect_playback_proc)pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, streamFlags, NULL, NULL);
16358  if (error != MA_PA_OK) {
16359  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream.", ma_result_from_pulse(error));
16360  goto on_error6;
16361  }
16362 
16363  while (((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pDevice->pulse.pStreamPlayback) != MA_PA_STREAM_READY) {
16364  error = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
16365  if (error < 0) {
16366  result = ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] The PulseAudio main loop returned an error while connecting the PulseAudio playback stream.", ma_result_from_pulse(error));
16367  goto on_error7;
16368  }
16369  }
16370 
16371  /* Internal format. */
16372  pActualSS = ((ma_pa_stream_get_sample_spec_proc)pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
16373  if (pActualSS != NULL) {
16374  /* If anything has changed between the requested and the actual sample spec, we need to update the buffer. */
16375  if (ss.format != pActualSS->format || ss.channels != pActualSS->channels || ss.rate != pActualSS->rate) {
16376  attr = ma_device__pa_buffer_attr_new(pDevice->playback.internalBufferSizeInFrames, pConfig->periods, pActualSS);
16377 
16378  pOP = ((ma_pa_stream_set_buffer_attr_proc)pContext->pulse.pa_stream_set_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, &attr, NULL, NULL);
16379  if (pOP != NULL) {
16380  ma_device__wait_for_operation__pulse(pDevice, pOP);
16381  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
16382  }
16383  }
16384 
16385  ss = *pActualSS;
16386  }
16387 
16388  pDevice->playback.internalFormat = ma_format_from_pulse(ss.format);
16389  pDevice->playback.internalChannels = ss.channels;
16390  pDevice->playback.internalSampleRate = ss.rate;
16391 
16392  /* Internal channel map. */
16393  pActualCMap = ((ma_pa_stream_get_channel_map_proc)pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
16394  if (pActualCMap != NULL) {
16395  cmap = *pActualCMap;
16396  }
16397  for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
16398  pDevice->playback.internalChannelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]);
16399  }
16400 
16401  /* Buffer. */
16402  pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
16403  if (pActualAttr != NULL) {
16404  attr = *pActualAttr;
16405  }
16406  pDevice->playback.internalBufferSizeInFrames = attr.maxlength / (ma_get_bytes_per_sample(pDevice->playback.internalFormat) * pDevice->playback.internalChannels);
16407  pDevice->playback.internalPeriods = /*pConfig->periods;*/attr.maxlength / attr.tlength;
16408  #ifdef MA_DEBUG_OUTPUT
16409  printf("[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalBufferSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDevice->playback.internalBufferSizeInFrames);
16410  #endif
16411 
16412  /* Name. */
16413  devPlayback = ((ma_pa_stream_get_device_name_proc)pContext->pulse.pa_stream_get_device_name)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
16414  if (devPlayback != NULL) {
16415  ma_pa_operation* pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pDevice->pulse.pPulseContext, devPlayback, ma_device_sink_name_callback, pDevice);
16416  if (pOP != NULL) {
16417  ma_device__wait_for_operation__pulse(pDevice, pOP);
16418  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
16419  }
16420  }
16421  }
16422 
16423  return MA_SUCCESS;
16424 
16425 
16426 on_error7:
16427  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
16428  ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
16429  }
16430 on_error6:
16431  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
16432  ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
16433  }
16434 on_error5:
16435  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
16436  ((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
16437  }
16438 on_error4:
16439  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
16440  ((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
16441  }
16442 on_error3: ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
16443 on_error2: ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
16444 on_error1: ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
16445 on_error0:
16446  return result;
16447 }
16448 
16449 
16450 void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData)
16451 {
16452  ma_bool32* pIsSuccessful = (ma_bool32*)pUserData;
16453  ma_assert(pIsSuccessful != NULL);
16454 
16455  *pIsSuccessful = (ma_bool32)success;
16456 
16457  (void)pStream; /* Unused. */
16458 }
16459 
16460 ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork)
16461 {
16462  ma_context* pContext = pDevice->pContext;
16463  ma_bool32 wasSuccessful;
16464  ma_pa_stream* pStream;
16465  ma_pa_operation* pOP;
16466  ma_result result;
16467 
16468  /* This should not be called with a duplex device type. */
16469  if (deviceType == ma_device_type_duplex) {
16470  return MA_INVALID_ARGS;
16471  }
16472 
16473  wasSuccessful = MA_FALSE;
16474 
16475  pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback);
16476  ma_assert(pStream != NULL);
16477 
16478  pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful);
16479  if (pOP == NULL) {
16480  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream.", (cork == 0) ? MA_FAILED_TO_START_BACKEND_DEVICE : MA_FAILED_TO_STOP_BACKEND_DEVICE);
16481  }
16482 
16483  result = ma_device__wait_for_operation__pulse(pDevice, pOP);
16484  ((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
16485 
16486  if (result != MA_SUCCESS) {
16487  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork.", result);
16488  }
16489 
16490  if (!wasSuccessful) {
16491  if (cork) {
16492  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to stop PulseAudio stream.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
16493  } else {
16494  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to start PulseAudio stream.", MA_FAILED_TO_START_BACKEND_DEVICE);
16495  }
16496  }
16497 
16498  return MA_SUCCESS;
16499 }
16500 
16501 ma_result ma_device_stop__pulse(ma_device* pDevice)
16502 {
16503  ma_result result;
16504  ma_bool32 wasSuccessful;
16505  ma_pa_operation* pOP;
16506 
16507  ma_assert(pDevice != NULL);
16508 
16509  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
16510  result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1);
16511  if (result != MA_SUCCESS) {
16512  return result;
16513  }
16514  }
16515 
16516  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
16517  /* The stream needs to be drained if it's a playback device. */
16518  pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful);
16519  if (pOP != NULL) {
16520  ma_device__wait_for_operation__pulse(pDevice, pOP);
16521  ((ma_pa_operation_unref_proc)pDevice->pContext->pulse.pa_operation_unref)(pOP);
16522  }
16523 
16524  result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1);
16525  if (result != MA_SUCCESS) {
16526  return result;
16527  }
16528  }
16529 
16530  return MA_SUCCESS;
16531 }
16532 
16533 ma_result ma_device_write__pulse(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
16534 {
16535  ma_uint32 totalFramesWritten;
16536 
16537  ma_assert(pDevice != NULL);
16538  ma_assert(pPCMFrames != NULL);
16539  ma_assert(frameCount > 0);
16540 
16541  if (pFramesWritten != NULL) {
16542  *pFramesWritten = 0;
16543  }
16544 
16545  totalFramesWritten = 0;
16546  while (totalFramesWritten < frameCount) {
16547  if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
16548  return MA_DEVICE_NOT_STARTED;
16549  }
16550 
16551  /* Place the data into the mapped buffer if we have one. */
16552  if (pDevice->pulse.pMappedBufferPlayback != NULL && pDevice->pulse.mappedBufferFramesRemainingPlayback > 0) {
16553  ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
16554  ma_uint32 mappedBufferFramesConsumed = pDevice->pulse.mappedBufferFramesCapacityPlayback - pDevice->pulse.mappedBufferFramesRemainingPlayback;
16555 
16556  void* pDst = (ma_uint8*)pDevice->pulse.pMappedBufferPlayback + (mappedBufferFramesConsumed * bpf);
16557  const void* pSrc = (const ma_uint8*)pPCMFrames + (totalFramesWritten * bpf);
16558  ma_uint32 framesToCopy = ma_min(pDevice->pulse.mappedBufferFramesRemainingPlayback, (frameCount - totalFramesWritten));
16559  ma_copy_memory(pDst, pSrc, framesToCopy * bpf);
16560 
16561  pDevice->pulse.mappedBufferFramesRemainingPlayback -= framesToCopy;
16562  totalFramesWritten += framesToCopy;
16563  }
16564 
16565  /*
16566  Getting here means we've run out of data in the currently mapped chunk. We need to write this to the device and then try
16567  mapping another chunk. If this fails we need to wait for space to become available.
16568  */
16569  if (pDevice->pulse.mappedBufferFramesCapacityPlayback > 0 && pDevice->pulse.mappedBufferFramesRemainingPlayback == 0) {
16570  size_t nbytes = pDevice->pulse.mappedBufferFramesCapacityPlayback * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
16571 
16572  int error = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, pDevice->pulse.pMappedBufferPlayback, nbytes, NULL, 0, MA_PA_SEEK_RELATIVE);
16573  if (error < 0) {
16574  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to write data to the PulseAudio stream.", ma_result_from_pulse(error));
16575  }
16576 
16577  pDevice->pulse.pMappedBufferPlayback = NULL;
16578  pDevice->pulse.mappedBufferFramesRemainingPlayback = 0;
16579  pDevice->pulse.mappedBufferFramesCapacityPlayback = 0;
16580  }
16581 
16582  ma_assert(totalFramesWritten <= frameCount);
16583  if (totalFramesWritten == frameCount) {
16584  break;
16585  }
16586 
16587  /* Getting here means we need to map a new buffer. If we don't have enough space we need to wait for more. */
16588  for (;;) {
16589  size_t writableSizeInBytes;
16590 
16591  /* If the device has been corked, don't try to continue. */
16592  if (((ma_pa_stream_is_corked_proc)pDevice->pContext->pulse.pa_stream_is_corked)((ma_pa_stream*)pDevice->pulse.pStreamPlayback)) {
16593  break;
16594  }
16595 
16596  writableSizeInBytes = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
16597  if (writableSizeInBytes != (size_t)-1) {
16598  /*size_t periodSizeInBytes = (pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods) * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);*/
16599  if (writableSizeInBytes > 0) {
16600  /* Data is avaialable. */
16601  size_t bytesToMap = writableSizeInBytes;
16602  int error = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, &pDevice->pulse.pMappedBufferPlayback, &bytesToMap);
16603  if (error < 0) {
16604  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to map write buffer.", ma_result_from_pulse(error));
16605  }
16606 
16607  pDevice->pulse.mappedBufferFramesCapacityPlayback = bytesToMap / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
16608  pDevice->pulse.mappedBufferFramesRemainingPlayback = pDevice->pulse.mappedBufferFramesCapacityPlayback;
16609 
16610  break;
16611  } else {
16612  /* No data available. Need to wait for more. */
16613  int error = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
16614  if (error < 0) {
16615  return ma_result_from_pulse(error);
16616  }
16617 
16618  continue;
16619  }
16620  } else {
16621  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to query the stream's writable size.", MA_ERROR);
16622  }
16623  }
16624  }
16625 
16626  if (pFramesWritten != NULL) {
16627  *pFramesWritten = totalFramesWritten;
16628  }
16629 
16630  return MA_SUCCESS;
16631 }
16632 
16633 ma_result ma_device_read__pulse(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
16634 {
16635  ma_uint32 totalFramesRead;
16636 
16637  ma_assert(pDevice != NULL);
16638  ma_assert(pPCMFrames != NULL);
16639  ma_assert(frameCount > 0);
16640 
16641  if (pFramesRead != NULL) {
16642  *pFramesRead = 0;
16643  }
16644 
16645  totalFramesRead = 0;
16646  while (totalFramesRead < frameCount) {
16647  if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
16648  return MA_DEVICE_NOT_STARTED;
16649  }
16650 
16651  /* If a buffer is mapped we need to write to that first. Once it's consumed we reset the event and unmap it. */
16652  if (pDevice->pulse.pMappedBufferCapture != NULL && pDevice->pulse.mappedBufferFramesRemainingCapture > 0) {
16653  ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
16654  ma_uint32 mappedBufferFramesConsumed = pDevice->pulse.mappedBufferFramesCapacityCapture - pDevice->pulse.mappedBufferFramesRemainingCapture;
16655 
16656  ma_uint32 framesToCopy = ma_min(pDevice->pulse.mappedBufferFramesRemainingCapture, (frameCount - totalFramesRead));
16657  void* pDst = (ma_uint8*)pPCMFrames + (totalFramesRead * bpf);
16658 
16659  /*
16660  This little bit of logic here is specifically for PulseAudio and it's hole management. The buffer pointer will be set to NULL
16661  when the current fragment is a hole. For a hole we just output silence.
16662  */
16663  if (pDevice->pulse.pMappedBufferCapture != NULL) {
16664  const void* pSrc = (const ma_uint8*)pDevice->pulse.pMappedBufferCapture + (mappedBufferFramesConsumed * bpf);
16665  ma_copy_memory(pDst, pSrc, framesToCopy * bpf);
16666  } else {
16667  ma_zero_memory(pDst, framesToCopy * bpf);
16668  }
16669 
16670  pDevice->pulse.mappedBufferFramesRemainingCapture -= framesToCopy;
16671  totalFramesRead += framesToCopy;
16672  }
16673 
16674  /*
16675  Getting here means we've run out of data in the currently mapped chunk. We need to drop this from the device and then try
16676  mapping another chunk. If this fails we need to wait for data to become available.
16677  */
16678  if (pDevice->pulse.mappedBufferFramesCapacityCapture > 0 && pDevice->pulse.mappedBufferFramesRemainingCapture == 0) {
16679  int error = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
16680  if (error != 0) {
16681  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to drop fragment.", ma_result_from_pulse(error));
16682  }
16683 
16684  pDevice->pulse.pMappedBufferCapture = NULL;
16685  pDevice->pulse.mappedBufferFramesRemainingCapture = 0;
16686  pDevice->pulse.mappedBufferFramesCapacityCapture = 0;
16687  }
16688 
16689  ma_assert(totalFramesRead <= frameCount);
16690  if (totalFramesRead == frameCount) {
16691  break;
16692  }
16693 
16694  /* Getting here means we need to map a new buffer. If we don't have enough data we wait for more. */
16695  for (;;) {
16696  size_t readableSizeInBytes;
16697 
16698  if (ma_device__get_state(pDevice) != MA_STATE_STARTED) {
16699  break;
16700  }
16701 
16702  /* If the device has been corked, don't try to continue. */
16703  if (((ma_pa_stream_is_corked_proc)pDevice->pContext->pulse.pa_stream_is_corked)((ma_pa_stream*)pDevice->pulse.pStreamCapture)) {
16704  break;
16705  }
16706 
16707  readableSizeInBytes = ((ma_pa_stream_readable_size_proc)pDevice->pContext->pulse.pa_stream_readable_size)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
16708  if (readableSizeInBytes != (size_t)-1) {
16709  /*size_t periodSizeInBytes = (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods) * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);*/
16710  if (readableSizeInBytes > 0) {
16711  /* Data is avaialable. */
16712  size_t bytesMapped = (size_t)-1;
16713  int error = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)((ma_pa_stream*)pDevice->pulse.pStreamCapture, &pDevice->pulse.pMappedBufferCapture, &bytesMapped);
16714  if (error < 0) {
16715  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to peek capture buffer.", ma_result_from_pulse(error));
16716  }
16717 
16718  if (pDevice->pulse.pMappedBufferCapture == NULL && bytesMapped == 0) {
16719  /* Nothing available. This shouldn't happen because we checked earlier with pa_stream_readable_size(). I'm going to throw an error in this case. */
16720  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Nothing available after peeking capture buffer.", MA_ERROR);
16721  }
16722 
16723  pDevice->pulse.mappedBufferFramesCapacityCapture = bytesMapped / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
16724  pDevice->pulse.mappedBufferFramesRemainingCapture = pDevice->pulse.mappedBufferFramesCapacityCapture;
16725 
16726  break;
16727  } else {
16728  /* No data available. Need to wait for more. */
16729 
16730  /*
16731  I have had reports of a deadlock in this part of the code. I have reproduced this when using the "Built-in Audio Analogue Stereo" device without
16732  an actual microphone connected. I'm experimenting here by not blocking in pa_mainloop_iterate() and instead sleep for a bit when there are no
16733  dispatches.
16734  */
16735  int error = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 0, NULL);
16736  if (error < 0) {
16737  return ma_result_from_pulse(error);
16738  }
16739 
16740  /* Sleep for a bit if nothing was dispatched. */
16741  if (error == 0) {
16742  ma_sleep(1);
16743  }
16744 
16745  continue;
16746  }
16747  } else {
16748  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to query the stream's readable size.", MA_ERROR);
16749  }
16750  }
16751  }
16752 
16753  if (pFramesRead != NULL) {
16754  *pFramesRead = totalFramesRead;
16755  }
16756 
16757  return MA_SUCCESS;
16758 }
16759 
16760 ma_result ma_device_main_loop__pulse(ma_device* pDevice)
16761 {
16763  ma_bool32 exitLoop = MA_FALSE;
16764 
16765  ma_assert(pDevice != NULL);
16766 
16767  /* The stream needs to be uncorked first. We do this at the top for both capture and playback for PulseAudio. */
16768  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
16769  result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0);
16770  if (result != MA_SUCCESS) {
16771  return result;
16772  }
16773  }
16774  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
16775  result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0);
16776  if (result != MA_SUCCESS) {
16777  return result;
16778  }
16779  }
16780 
16781 
16782  while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
16783  switch (pDevice->type)
16784  {
16785  case ma_device_type_duplex:
16786  {
16787  /* The process is: device_read -> convert -> callback -> convert -> device_write */
16788  ma_uint8 capturedDeviceData[8192];
16789  ma_uint8 playbackDeviceData[8192];
16790  ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
16791  ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
16792 
16793  ma_uint32 totalFramesProcessed = 0;
16794  ma_uint32 periodSizeInFrames = ma_min(pDevice->capture.internalBufferSizeInFrames/pDevice->capture.internalPeriods, pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods);
16795 
16796  while (totalFramesProcessed < periodSizeInFrames) {
16797  ma_uint32 framesRemaining = periodSizeInFrames - totalFramesProcessed;
16798  ma_uint32 framesProcessed;
16799  ma_uint32 framesToProcess = framesRemaining;
16800  if (framesToProcess > capturedDeviceDataCapInFrames) {
16801  framesToProcess = capturedDeviceDataCapInFrames;
16802  }
16803 
16804  result = ma_device_read__pulse(pDevice, capturedDeviceData, framesToProcess, &framesProcessed);
16805  if (result != MA_SUCCESS) {
16806  exitLoop = MA_TRUE;
16807  break;
16808  }
16809 
16810  pDevice->capture._dspFrameCount = framesToProcess;
16811  pDevice->capture._dspFrames = capturedDeviceData;
16812 
16813  for (;;) {
16814  ma_uint8 capturedData[8192];
16815  ma_uint8 playbackData[8192];
16816  ma_uint32 capturedDataCapInFrames = sizeof(capturedData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
16817  ma_uint32 playbackDataCapInFrames = sizeof(playbackData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
16818 
16819  ma_uint32 capturedFramesToTryProcessing = ma_min(capturedDataCapInFrames, playbackDataCapInFrames);
16820  ma_uint32 capturedFramesToProcess = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, capturedData, capturedFramesToTryProcessing);
16821  if (capturedFramesToProcess == 0) {
16822  break; /* Don't fire the data callback with zero frames. */
16823  }
16824 
16825  ma_device__on_data(pDevice, playbackData, capturedData, capturedFramesToProcess);
16826 
16827  /* At this point the playbackData buffer should be holding data that needs to be written to the device. */
16828  pDevice->playback._dspFrameCount = capturedFramesToProcess;
16829  pDevice->playback._dspFrames = playbackData;
16830  for (;;) {
16831  ma_uint32 playbackDeviceFramesCount = (ma_uint32)ma_pcm_converter_read(&pDevice->playback.converter, playbackDeviceData, playbackDeviceDataCapInFrames);
16832  if (playbackDeviceFramesCount == 0) {
16833  break;
16834  }
16835 
16836  result = ma_device_write__pulse(pDevice, playbackDeviceData, playbackDeviceFramesCount, NULL);
16837  if (result != MA_SUCCESS) {
16838  exitLoop = MA_TRUE;
16839  break;
16840  }
16841 
16842  if (playbackDeviceFramesCount < playbackDeviceDataCapInFrames) {
16843  break;
16844  }
16845  }
16846 
16847  if (capturedFramesToProcess < capturedFramesToTryProcessing) {
16848  break;
16849  }
16850 
16851  /* In case an error happened from ma_device_write2__alsa()... */
16852  if (result != MA_SUCCESS) {
16853  exitLoop = MA_TRUE;
16854  break;
16855  }
16856  }
16857 
16858  totalFramesProcessed += framesProcessed;
16859  }
16860  } break;
16861 
16863  {
16864  ma_uint8 intermediaryBuffer[8192];
16865  ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
16866  ma_uint32 periodSizeInFrames = pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods;
16867  ma_uint32 framesReadThisPeriod = 0;
16868  while (framesReadThisPeriod < periodSizeInFrames) {
16869  ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
16870  ma_uint32 framesProcessed;
16871  ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
16872  if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
16873  framesToReadThisIteration = intermediaryBufferSizeInFrames;
16874  }
16875 
16876  result = ma_device_read__pulse(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
16877  if (result != MA_SUCCESS) {
16878  exitLoop = MA_TRUE;
16879  break;
16880  }
16881 
16882  ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
16883 
16884  framesReadThisPeriod += framesProcessed;
16885  }
16886  } break;
16887 
16889  {
16890  ma_uint8 intermediaryBuffer[8192];
16891  ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
16892  ma_uint32 periodSizeInFrames = pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods;
16893  ma_uint32 framesWrittenThisPeriod = 0;
16894  while (framesWrittenThisPeriod < periodSizeInFrames) {
16895  ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
16896  ma_uint32 framesProcessed;
16897  ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
16898  if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
16899  framesToWriteThisIteration = intermediaryBufferSizeInFrames;
16900  }
16901 
16902  ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
16903 
16904  result = ma_device_write__pulse(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
16905  if (result != MA_SUCCESS) {
16906  exitLoop = MA_TRUE;
16907  break;
16908  }
16909 
16910  framesWrittenThisPeriod += framesProcessed;
16911  }
16912  } break;
16913 
16914  /* To silence a warning. Will never hit this. */
16916  default: break;
16917  }
16918  }
16919 
16920  /* Here is where the device needs to be stopped. */
16921  ma_device_stop__pulse(pDevice);
16922 
16923  return result;
16924 }
16925 
16926 
16927 ma_result ma_context_uninit__pulse(ma_context* pContext)
16928 {
16929  ma_assert(pContext != NULL);
16930  ma_assert(pContext->backend == ma_backend_pulseaudio);
16931 
16932  ma_free(pContext->pulse.pServerName);
16933  pContext->pulse.pServerName = NULL;
16934 
16935  ma_free(pContext->pulse.pApplicationName);
16936  pContext->pulse.pApplicationName = NULL;
16937 
16938 #ifndef MA_NO_RUNTIME_LINKING
16939  ma_dlclose(pContext, pContext->pulse.pulseSO);
16940 #endif
16941 
16942  return MA_SUCCESS;
16943 }
16944 
16945 ma_result ma_context_init__pulse(const ma_context_config* pConfig, ma_context* pContext)
16946 {
16947 #ifndef MA_NO_RUNTIME_LINKING
16948  const char* libpulseNames[] = {
16949  "libpulse.so",
16950  "libpulse.so.0"
16951  };
16952  size_t i;
16953 
16954  for (i = 0; i < ma_countof(libpulseNames); ++i) {
16955  pContext->pulse.pulseSO = ma_dlopen(pContext, libpulseNames[i]);
16956  if (pContext->pulse.pulseSO != NULL) {
16957  break;
16958  }
16959  }
16960 
16961  if (pContext->pulse.pulseSO == NULL) {
16962  return MA_NO_BACKEND;
16963  }
16964 
16965  pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_new");
16966  pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_free");
16967  pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_get_api");
16968  pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_iterate");
16969  pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_wakeup");
16970  pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_new");
16971  pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_unref");
16972  pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_connect");
16973  pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_disconnect");
16974  pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_set_state_callback");
16975  pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_state");
16976  pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_list");
16977  pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_list");
16978  pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name");
16979  pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_by_name");
16980  pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_unref");
16981  pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_get_state");
16982  pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_init_extend");
16983  pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_valid");
16984  pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_compatible");
16985  pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_new");
16986  pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_unref");
16987  pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_playback");
16988  pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_record");
16989  pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_disconnect");
16990  pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_state");
16991  pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_sample_spec");
16992  pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_channel_map");
16993  pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_buffer_attr");
16994  pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_buffer_attr");
16995  pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_device_name");
16996  pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback");
16997  pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback");
16998  pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush");
16999  pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain");
17000  pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_corked");
17001  pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_cork");
17002  pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_trigger");
17003  pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_begin_write");
17004  pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_write");
17005  pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_peek");
17006  pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drop");
17007  pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_writable_size");
17008  pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_readable_size");
17009 #else
17010  /* This strange assignment system is just for type safety. */
17011  ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new;
17012  ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free;
17013  ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api;
17014  ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate;
17015  ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup;
17016  ma_pa_context_new_proc _pa_context_new = pa_context_new;
17017  ma_pa_context_unref_proc _pa_context_unref = pa_context_unref;
17018  ma_pa_context_connect_proc _pa_context_connect = pa_context_connect;
17019  ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect;
17020  ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback;
17021  ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state;
17022  ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list;
17023  ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list;
17024  ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name;
17025  ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name;
17026  ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref;
17027  ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state;
17028  ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend;
17029  ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid;
17030  ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible;
17031  ma_pa_stream_new_proc _pa_stream_new = pa_stream_new;
17032  ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref;
17033  ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback;
17034  ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record;
17035  ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect;
17036  ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state;
17037  ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec;
17038  ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map;
17039  ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr;
17040  ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr;
17041  ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name;
17042  ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback;
17043  ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
17044  ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush;
17045  ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain;
17046  ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked;
17047  ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork;
17048  ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger;
17049  ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write;
17050  ma_pa_stream_write_proc _pa_stream_write = pa_stream_write;
17051  ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek;
17052  ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop;
17053  ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size;
17054  ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size;
17055 
17056  pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new;
17057  pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free;
17058  pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api;
17059  pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate;
17060  pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup;
17061  pContext->pulse.pa_context_new = (ma_proc)_pa_context_new;
17062  pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref;
17063  pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect;
17064  pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect;
17065  pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback;
17066  pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state;
17067  pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list;
17068  pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list;
17069  pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name;
17070  pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name;
17071  pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref;
17072  pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state;
17073  pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend;
17074  pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid;
17075  pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible;
17076  pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new;
17077  pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref;
17078  pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback;
17079  pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record;
17080  pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect;
17081  pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state;
17082  pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec;
17083  pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map;
17084  pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr;
17085  pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr;
17086  pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name;
17087  pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback;
17088  pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback;
17089  pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush;
17090  pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain;
17091  pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked;
17092  pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork;
17093  pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger;
17094  pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write;
17095  pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write;
17096  pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek;
17097  pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop;
17098  pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size;
17099  pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size;
17100 #endif
17101 
17102  pContext->onUninit = ma_context_uninit__pulse;
17103  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__pulse;
17104  pContext->onEnumDevices = ma_context_enumerate_devices__pulse;
17105  pContext->onGetDeviceInfo = ma_context_get_device_info__pulse;
17106  pContext->onDeviceInit = ma_device_init__pulse;
17107  pContext->onDeviceUninit = ma_device_uninit__pulse;
17108  pContext->onDeviceStart = NULL;
17109  pContext->onDeviceStop = NULL;
17110  pContext->onDeviceMainLoop = ma_device_main_loop__pulse;
17111 
17112  if (pConfig->pulse.pApplicationName) {
17113  pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName);
17114  }
17115  if (pConfig->pulse.pServerName) {
17116  pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName);
17117  }
17118  pContext->pulse.tryAutoSpawn = pConfig->pulse.tryAutoSpawn;
17119 
17120  /*
17121  Although we have found the libpulse library, it doesn't necessarily mean PulseAudio is useable. We need to initialize
17122  and connect a dummy PulseAudio context to test PulseAudio's usability.
17123  */
17124  {
17125  ma_pa_mainloop* pMainLoop;
17126  ma_pa_mainloop_api* pAPI;
17127  ma_pa_context* pPulseContext;
17128  int error;
17129 
17130  pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
17131  if (pMainLoop == NULL) {
17132  ma_free(pContext->pulse.pServerName);
17133  ma_free(pContext->pulse.pApplicationName);
17134  #ifndef MA_NO_RUNTIME_LINKING
17135  ma_dlclose(pContext, pContext->pulse.pulseSO);
17136  #endif
17137  return MA_NO_BACKEND;
17138  }
17139 
17140  pAPI = ((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)(pMainLoop);
17141  if (pAPI == NULL) {
17142  ma_free(pContext->pulse.pServerName);
17143  ma_free(pContext->pulse.pApplicationName);
17144  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
17145  #ifndef MA_NO_RUNTIME_LINKING
17146  ma_dlclose(pContext, pContext->pulse.pulseSO);
17147  #endif
17148  return MA_NO_BACKEND;
17149  }
17150 
17151  pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(pAPI, pContext->pulse.pApplicationName);
17152  if (pPulseContext == NULL) {
17153  ma_free(pContext->pulse.pServerName);
17154  ma_free(pContext->pulse.pApplicationName);
17155  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
17156  #ifndef MA_NO_RUNTIME_LINKING
17157  ma_dlclose(pContext, pContext->pulse.pulseSO);
17158  #endif
17159  return MA_NO_BACKEND;
17160  }
17161 
17162  error = ((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)(pPulseContext, pContext->pulse.pServerName, 0, NULL);
17163  if (error != MA_PA_OK) {
17164  ma_free(pContext->pulse.pServerName);
17165  ma_free(pContext->pulse.pApplicationName);
17166  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
17167  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
17168  #ifndef MA_NO_RUNTIME_LINKING
17169  ma_dlclose(pContext, pContext->pulse.pulseSO);
17170  #endif
17171  return MA_NO_BACKEND;
17172  }
17173 
17174  ((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)(pPulseContext);
17175  ((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)(pPulseContext);
17176  ((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)(pMainLoop);
17177  }
17178 
17179  return MA_SUCCESS;
17180 }
17181 #endif
17182 
17183 
17184 /******************************************************************************
17185 
17186 JACK Backend
17187 
17188 ******************************************************************************/
17189 #ifdef MA_HAS_JACK
17190 
17191 /* It is assumed jack.h is available when compile-time linking is being used. */
17192 #ifdef MA_NO_RUNTIME_LINKING
17193 #include <jack/jack.h>
17194 
17195 typedef jack_nframes_t ma_jack_nframes_t;
17196 typedef jack_options_t ma_jack_options_t;
17197 typedef jack_status_t ma_jack_status_t;
17198 typedef jack_client_t ma_jack_client_t;
17199 typedef jack_port_t ma_jack_port_t;
17200 typedef JackProcessCallback ma_JackProcessCallback;
17201 typedef JackBufferSizeCallback ma_JackBufferSizeCallback;
17202 typedef JackShutdownCallback ma_JackShutdownCallback;
17203 #define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE
17204 #define ma_JackNoStartServer JackNoStartServer
17205 #define ma_JackPortIsInput JackPortIsInput
17206 #define ma_JackPortIsOutput JackPortIsOutput
17207 #define ma_JackPortIsPhysical JackPortIsPhysical
17208 #else
17209 typedef ma_uint32 ma_jack_nframes_t;
17210 typedef int ma_jack_options_t;
17211 typedef int ma_jack_status_t;
17212 typedef struct ma_jack_client_t ma_jack_client_t;
17213 typedef struct ma_jack_port_t ma_jack_port_t;
17214 typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg);
17215 typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg);
17216 typedef void (* ma_JackShutdownCallback) (void* arg);
17217 #define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio"
17218 #define ma_JackNoStartServer 1
17219 #define ma_JackPortIsInput 1
17220 #define ma_JackPortIsOutput 2
17221 #define ma_JackPortIsPhysical 4
17222 #endif
17223 
17224 typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...);
17225 typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client);
17226 typedef int (* ma_jack_client_name_size_proc) ();
17227 typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg);
17228 typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg);
17229 typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg);
17230 typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client);
17231 typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client);
17232 typedef const char** (* ma_jack_get_ports_proc) (ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags);
17233 typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client);
17234 typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client);
17235 typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port);
17236 typedef ma_jack_port_t* (* ma_jack_port_register_proc) (ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size);
17237 typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port);
17238 typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes);
17239 typedef void (* ma_jack_free_proc) (void* ptr);
17240 
17241 ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient)
17242 {
17243  size_t maxClientNameSize;
17244  char clientName[256];
17245  ma_jack_status_t status;
17246  ma_jack_client_t* pClient;
17247 
17248  ma_assert(pContext != NULL);
17249  ma_assert(ppClient != NULL);
17250 
17251  if (ppClient) {
17252  *ppClient = NULL;
17253  }
17254 
17255  maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)(); /* Includes null terminator. */
17256  ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1);
17257 
17258  pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? 0 : ma_JackNoStartServer, &status, NULL);
17259  if (pClient == NULL) {
17261  }
17262 
17263  if (ppClient) {
17264  *ppClient = pClient;
17265  }
17266 
17267  return MA_SUCCESS;
17268 }
17269 
17270 ma_bool32 ma_context_is_device_id_equal__jack(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
17271 {
17272  ma_assert(pContext != NULL);
17273  ma_assert(pID0 != NULL);
17274  ma_assert(pID1 != NULL);
17275  (void)pContext;
17276 
17277  return pID0->jack == pID1->jack;
17278 }
17279 
17280 ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
17281 {
17282  ma_bool32 cbResult = MA_TRUE;
17283 
17284  ma_assert(pContext != NULL);
17285  ma_assert(callback != NULL);
17286 
17287  /* Playback. */
17288  if (cbResult) {
17289  ma_device_info deviceInfo;
17290  ma_zero_object(&deviceInfo);
17291  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
17292  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
17293  }
17294 
17295  /* Capture. */
17296  if (cbResult) {
17297  ma_device_info deviceInfo;
17298  ma_zero_object(&deviceInfo);
17299  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
17300  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
17301  }
17302 
17303  return MA_SUCCESS;
17304 }
17305 
17306 ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
17307 {
17308  ma_jack_client_t* pClient;
17309  ma_result result;
17310  const char** ppPorts;
17311 
17312  ma_assert(pContext != NULL);
17313 
17314  /* No exclusive mode with the JACK backend. */
17315  if (shareMode == ma_share_mode_exclusive) {
17317  }
17318 
17319  if (pDeviceID != NULL && pDeviceID->jack != 0) {
17320  return MA_NO_DEVICE; /* Don't know the device. */
17321  }
17322 
17323  /* Name / Description */
17324  if (deviceType == ma_device_type_playback) {
17325  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
17326  } else {
17327  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
17328  }
17329 
17330  /* Jack only supports f32 and has a specific channel count and sample rate. */
17331  pDeviceInfo->formatCount = 1;
17332  pDeviceInfo->formats[0] = ma_format_f32;
17333 
17334  /* The channel count and sample rate can only be determined by opening the device. */
17335  result = ma_context_open_client__jack(pContext, &pClient);
17336  if (result != MA_SUCCESS) {
17337  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
17338  }
17339 
17340  pDeviceInfo->minSampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient);
17341  pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
17342 
17343  pDeviceInfo->minChannels = 0;
17344  pDeviceInfo->maxChannels = 0;
17345 
17346  ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, NULL, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput));
17347  if (ppPorts == NULL) {
17348  ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
17349  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
17350  }
17351 
17352  while (ppPorts[pDeviceInfo->minChannels] != NULL) {
17353  pDeviceInfo->minChannels += 1;
17354  pDeviceInfo->maxChannels += 1;
17355  }
17356 
17357  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
17358  ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
17359 
17360  (void)pContext;
17361  return MA_SUCCESS;
17362 }
17363 
17364 
17365 void ma_device_uninit__jack(ma_device* pDevice)
17366 {
17367  ma_context* pContext;
17368 
17369  ma_assert(pDevice != NULL);
17370 
17371  pContext = pDevice->pContext;
17372  ma_assert(pContext != NULL);
17373 
17374  if (pDevice->jack.pClient != NULL) {
17375  ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient);
17376  }
17377 
17378  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
17379  ma_free(pDevice->jack.pIntermediaryBufferCapture);
17380  }
17381 
17382  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
17383  ma_free(pDevice->jack.pIntermediaryBufferPlayback);
17384  }
17385 
17386  if (pDevice->type == ma_device_type_duplex) {
17387  ma_pcm_rb_uninit(&pDevice->jack.duplexRB);
17388  }
17389 }
17390 
17391 void ma_device__jack_shutdown_callback(void* pUserData)
17392 {
17393  /* JACK died. Stop the device. */
17394  ma_device* pDevice = (ma_device*)pUserData;
17395  ma_assert(pDevice != NULL);
17396 
17397  ma_device_stop(pDevice);
17398 }
17399 
17400 int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData)
17401 {
17402  ma_device* pDevice = (ma_device*)pUserData;
17403  ma_assert(pDevice != NULL);
17404 
17405  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
17406  float* pNewBuffer = (float*)ma_realloc(pDevice->jack.pIntermediaryBufferCapture, frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat)));
17407  if (pNewBuffer == NULL) {
17408  return MA_OUT_OF_MEMORY;
17409  }
17410 
17411  pDevice->jack.pIntermediaryBufferCapture = pNewBuffer;
17412  pDevice->playback.internalBufferSizeInFrames = frameCount * pDevice->capture.internalPeriods;
17413  }
17414 
17415  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
17416  float* pNewBuffer = (float*)ma_realloc(pDevice->jack.pIntermediaryBufferPlayback, frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat)));
17417  if (pNewBuffer == NULL) {
17418  return MA_OUT_OF_MEMORY;
17419  }
17420 
17421  pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer;
17422  pDevice->playback.internalBufferSizeInFrames = frameCount * pDevice->playback.internalPeriods;
17423  }
17424 
17425  return 0;
17426 }
17427 
17428 int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData)
17429 {
17430  ma_device* pDevice;
17431  ma_context* pContext;
17432  ma_uint32 iChannel;
17433 
17434  pDevice = (ma_device*)pUserData;
17435  ma_assert(pDevice != NULL);
17436 
17437  pContext = pDevice->pContext;
17438  ma_assert(pContext != NULL);
17439 
17440  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
17441  /* Channels need to be interleaved. */
17442  for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
17443  const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.pPortsCapture[iChannel], frameCount);
17444  if (pSrc != NULL) {
17445  float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel;
17446  ma_jack_nframes_t iFrame;
17447  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
17448  *pDst = *pSrc;
17449 
17450  pDst += pDevice->capture.internalChannels;
17451  pSrc += 1;
17452  }
17453  }
17454  }
17455 
17456  if (pDevice->type == ma_device_type_duplex) {
17457  ma_device__handle_duplex_callback_capture(pDevice, frameCount, pDevice->jack.pIntermediaryBufferCapture, &pDevice->jack.duplexRB);
17458  } else {
17459  ma_device__send_frames_to_client(pDevice, frameCount, pDevice->jack.pIntermediaryBufferCapture);
17460  }
17461  }
17462 
17463  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
17464  if (pDevice->type == ma_device_type_duplex) {
17465  ma_device__handle_duplex_callback_playback(pDevice, frameCount, pDevice->jack.pIntermediaryBufferPlayback, &pDevice->jack.duplexRB);
17466  } else {
17467  ma_device__read_frames_from_client(pDevice, frameCount, pDevice->jack.pIntermediaryBufferPlayback);
17468  }
17469 
17470  /* Channels need to be deinterleaved. */
17471  for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
17472  float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.pPortsPlayback[iChannel], frameCount);
17473  if (pDst != NULL) {
17474  const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel;
17475  ma_jack_nframes_t iFrame;
17476  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
17477  *pDst = *pSrc;
17478 
17479  pDst += 1;
17480  pSrc += pDevice->playback.internalChannels;
17481  }
17482  }
17483  }
17484  }
17485 
17486  return 0;
17487 }
17488 
17489 ma_result ma_device_init__jack(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
17490 {
17491  ma_result result;
17492  ma_uint32 periods;
17493  ma_uint32 bufferSizeInFrames;
17494 
17495  ma_assert(pContext != NULL);
17496  ma_assert(pConfig != NULL);
17497  ma_assert(pDevice != NULL);
17498 
17499  if (pConfig->deviceType == ma_device_type_loopback) {
17501  }
17502 
17503  /* Only supporting default devices with JACK. */
17504  if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID != NULL && pConfig->playback.pDeviceID->jack != 0) ||
17505  ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.pDeviceID != NULL && pConfig->capture.pDeviceID->jack != 0)) {
17506  return MA_NO_DEVICE;
17507  }
17508 
17509  /* No exclusive mode with the JACK backend. */
17513  }
17514 
17515  /* Open the client. */
17516  result = ma_context_open_client__jack(pContext, (ma_jack_client_t**)&pDevice->jack.pClient);
17517  if (result != MA_SUCCESS) {
17518  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
17519  }
17520 
17521  /* Callbacks. */
17522  if (((ma_jack_set_process_callback_proc)pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) {
17523  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
17524  }
17525  if (((ma_jack_set_buffer_size_callback_proc)pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) {
17526  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
17527  }
17528 
17529  ((ma_jack_on_shutdown_proc)pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice);
17530 
17531 
17532  /* The buffer size in frames can change. */
17533  periods = pConfig->periods;
17534  bufferSizeInFrames = ((ma_jack_get_buffer_size_proc)pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient) * periods;
17535 
17536  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
17537  const char** ppPorts;
17538 
17539  pDevice->capture.internalFormat = ma_format_f32;
17540  pDevice->capture.internalChannels = 0;
17541  pDevice->capture.internalSampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
17542  ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
17543 
17544  ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, NULL, ma_JackPortIsPhysical | ma_JackPortIsOutput);
17545  if (ppPorts == NULL) {
17546  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
17547  }
17548 
17549  while (ppPorts[pDevice->capture.internalChannels] != NULL) {
17550  char name[64];
17551  ma_strcpy_s(name, sizeof(name), "capture");
17552  ma_itoa_s((int)pDevice->capture.internalChannels, name+7, sizeof(name)-7, 10); /* 7 = length of "capture" */
17553 
17554  pDevice->jack.pPortsCapture[pDevice->capture.internalChannels] = ((ma_jack_port_register_proc)pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0);
17555  if (pDevice->jack.pPortsCapture[pDevice->capture.internalChannels] == NULL) {
17556  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
17557  ma_device_uninit__jack(pDevice);
17558  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
17559  }
17560 
17561  pDevice->capture.internalChannels += 1;
17562  }
17563 
17564  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
17565 
17566  pDevice->capture.internalBufferSizeInFrames = bufferSizeInFrames;
17567  pDevice->capture.internalPeriods = periods;
17568 
17569  pDevice->jack.pIntermediaryBufferCapture = (float*)ma_malloc((pDevice->capture.internalBufferSizeInFrames/pDevice->capture.internalPeriods) * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat)));
17570  if (pDevice->jack.pIntermediaryBufferCapture == NULL) {
17571  ma_device_uninit__jack(pDevice);
17572  return MA_OUT_OF_MEMORY;
17573  }
17574  }
17575 
17576  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
17577  const char** ppPorts;
17578 
17579  pDevice->playback.internalFormat = ma_format_f32;
17580  pDevice->playback.internalChannels = 0;
17581  pDevice->playback.internalSampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
17582  ma_get_standard_channel_map(ma_standard_channel_map_alsa, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
17583 
17584  ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, NULL, ma_JackPortIsPhysical | ma_JackPortIsInput);
17585  if (ppPorts == NULL) {
17586  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
17587  }
17588 
17589  while (ppPorts[pDevice->playback.internalChannels] != NULL) {
17590  char name[64];
17591  ma_strcpy_s(name, sizeof(name), "playback");
17592  ma_itoa_s((int)pDevice->playback.internalChannels, name+8, sizeof(name)-8, 10); /* 8 = length of "playback" */
17593 
17594  pDevice->jack.pPortsPlayback[pDevice->playback.internalChannels] = ((ma_jack_port_register_proc)pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0);
17595  if (pDevice->jack.pPortsPlayback[pDevice->playback.internalChannels] == NULL) {
17596  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
17597  ma_device_uninit__jack(pDevice);
17598  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
17599  }
17600 
17601  pDevice->playback.internalChannels += 1;
17602  }
17603 
17604  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
17605 
17606  pDevice->playback.internalBufferSizeInFrames = bufferSizeInFrames;
17607  pDevice->playback.internalPeriods = periods;
17608 
17609  pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_malloc((pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods) * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat)));
17610  if (pDevice->jack.pIntermediaryBufferPlayback == NULL) {
17611  ma_device_uninit__jack(pDevice);
17612  return MA_OUT_OF_MEMORY;
17613  }
17614  }
17615 
17616  if (pDevice->type == ma_device_type_duplex) {
17617  ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_src(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalBufferSizeInFrames);
17618  result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->jack.duplexRB);
17619  if (result != MA_SUCCESS) {
17620  ma_device_uninit__jack(pDevice);
17621  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to initialize ring buffer.", result);
17622  }
17623 
17624  /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */
17625  {
17626  ma_uint32 marginSizeInFrames = rbSizeInFrames / pDevice->capture.internalPeriods;
17627  void* pMarginData;
17628  ma_pcm_rb_acquire_write(&pDevice->jack.duplexRB, &marginSizeInFrames, &pMarginData);
17629  {
17630  ma_zero_memory(pMarginData, marginSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
17631  }
17632  ma_pcm_rb_commit_write(&pDevice->jack.duplexRB, marginSizeInFrames, pMarginData);
17633  }
17634  }
17635 
17636  return MA_SUCCESS;
17637 }
17638 
17639 
17640 ma_result ma_device_start__jack(ma_device* pDevice)
17641 {
17642  ma_context* pContext = pDevice->pContext;
17643  int resultJACK;
17644  size_t i;
17645 
17646  resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient);
17647  if (resultJACK != 0) {
17648  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client.", MA_FAILED_TO_START_BACKEND_DEVICE);
17649  }
17650 
17651  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
17652  const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, NULL, ma_JackPortIsPhysical | ma_JackPortIsOutput);
17653  if (ppServerPorts == NULL) {
17654  ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
17655  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.", MA_ERROR);
17656  }
17657 
17658  for (i = 0; ppServerPorts[i] != NULL; ++i) {
17659  const char* pServerPort = ppServerPorts[i];
17660  const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.pPortsCapture[i]);
17661 
17662  resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort);
17663  if (resultJACK != 0) {
17664  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
17665  ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
17666  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.", MA_ERROR);
17667  }
17668  }
17669 
17670  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
17671  }
17672 
17673  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
17674  const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, NULL, ma_JackPortIsPhysical | ma_JackPortIsInput);
17675  if (ppServerPorts == NULL) {
17676  ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
17677  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.", MA_ERROR);
17678  }
17679 
17680  for (i = 0; ppServerPorts[i] != NULL; ++i) {
17681  const char* pServerPort = ppServerPorts[i];
17682  const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.pPortsPlayback[i]);
17683 
17684  resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort);
17685  if (resultJACK != 0) {
17686  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
17687  ((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
17688  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.", MA_ERROR);
17689  }
17690  }
17691 
17692  ((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
17693  }
17694 
17695  return MA_SUCCESS;
17696 }
17697 
17698 ma_result ma_device_stop__jack(ma_device* pDevice)
17699 {
17700  ma_context* pContext = pDevice->pContext;
17701  ma_stop_proc onStop;
17702 
17703  if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) {
17704  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.", MA_ERROR);
17705  }
17706 
17707  onStop = pDevice->onStop;
17708  if (onStop) {
17709  onStop(pDevice);
17710  }
17711 
17712  return MA_SUCCESS;
17713 }
17714 
17715 
17716 ma_result ma_context_uninit__jack(ma_context* pContext)
17717 {
17718  ma_assert(pContext != NULL);
17719  ma_assert(pContext->backend == ma_backend_jack);
17720 
17721  ma_free(pContext->jack.pClientName);
17722  pContext->jack.pClientName = NULL;
17723 
17724 #ifndef MA_NO_RUNTIME_LINKING
17725  ma_dlclose(pContext, pContext->jack.jackSO);
17726 #endif
17727 
17728  return MA_SUCCESS;
17729 }
17730 
17731 ma_result ma_context_init__jack(const ma_context_config* pConfig, ma_context* pContext)
17732 {
17733 #ifndef MA_NO_RUNTIME_LINKING
17734  const char* libjackNames[] = {
17735 #ifdef MA_WIN32
17736  "libjack.dll"
17737 #else
17738  "libjack.so",
17739  "libjack.so.0"
17740 #endif
17741  };
17742  size_t i;
17743 
17744  for (i = 0; i < ma_countof(libjackNames); ++i) {
17745  pContext->jack.jackSO = ma_dlopen(pContext, libjackNames[i]);
17746  if (pContext->jack.jackSO != NULL) {
17747  break;
17748  }
17749  }
17750 
17751  if (pContext->jack.jackSO == NULL) {
17752  return MA_NO_BACKEND;
17753  }
17754 
17755  pContext->jack.jack_client_open = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_open");
17756  pContext->jack.jack_client_close = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_close");
17757  pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_name_size");
17758  pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_process_callback");
17759  pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_buffer_size_callback");
17760  pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_on_shutdown");
17761  pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_sample_rate");
17762  pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_buffer_size");
17763  pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_ports");
17764  pContext->jack.jack_activate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_activate");
17765  pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_deactivate");
17766  pContext->jack.jack_connect = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_connect");
17767  pContext->jack.jack_port_register = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_register");
17768  pContext->jack.jack_port_name = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_name");
17769  pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_get_buffer");
17770  pContext->jack.jack_free = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_free");
17771 #else
17772  /*
17773  This strange assignment system is here just to ensure type safety of miniaudio's function pointer
17774  types. If anything differs slightly the compiler should throw a warning.
17775  */
17776  ma_jack_client_open_proc _jack_client_open = jack_client_open;
17777  ma_jack_client_close_proc _jack_client_close = jack_client_close;
17778  ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size;
17779  ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback;
17780  ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback;
17781  ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown;
17782  ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate;
17783  ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size;
17784  ma_jack_get_ports_proc _jack_get_ports = jack_get_ports;
17785  ma_jack_activate_proc _jack_activate = jack_activate;
17786  ma_jack_deactivate_proc _jack_deactivate = jack_deactivate;
17787  ma_jack_connect_proc _jack_connect = jack_connect;
17788  ma_jack_port_register_proc _jack_port_register = jack_port_register;
17789  ma_jack_port_name_proc _jack_port_name = jack_port_name;
17790  ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer;
17791  ma_jack_free_proc _jack_free = jack_free;
17792 
17793  pContext->jack.jack_client_open = (ma_proc)_jack_client_open;
17794  pContext->jack.jack_client_close = (ma_proc)_jack_client_close;
17795  pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size;
17796  pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback;
17797  pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback;
17798  pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown;
17799  pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate;
17800  pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size;
17801  pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports;
17802  pContext->jack.jack_activate = (ma_proc)_jack_activate;
17803  pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate;
17804  pContext->jack.jack_connect = (ma_proc)_jack_connect;
17805  pContext->jack.jack_port_register = (ma_proc)_jack_port_register;
17806  pContext->jack.jack_port_name = (ma_proc)_jack_port_name;
17807  pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer;
17808  pContext->jack.jack_free = (ma_proc)_jack_free;
17809 #endif
17810 
17811  pContext->isBackendAsynchronous = MA_TRUE;
17812 
17813  pContext->onUninit = ma_context_uninit__jack;
17814  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__jack;
17815  pContext->onEnumDevices = ma_context_enumerate_devices__jack;
17816  pContext->onGetDeviceInfo = ma_context_get_device_info__jack;
17817  pContext->onDeviceInit = ma_device_init__jack;
17818  pContext->onDeviceUninit = ma_device_uninit__jack;
17819  pContext->onDeviceStart = ma_device_start__jack;
17820  pContext->onDeviceStop = ma_device_stop__jack;
17821 
17822  if (pConfig->jack.pClientName != NULL) {
17823  pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName);
17824  }
17825  pContext->jack.tryStartServer = pConfig->jack.tryStartServer;
17826 
17827  /*
17828  Getting here means the JACK library is installed, but it doesn't necessarily mean it's usable. We need to quickly test this by connecting
17829  a temporary client.
17830  */
17831  {
17832  ma_jack_client_t* pDummyClient;
17833  ma_result result = ma_context_open_client__jack(pContext, &pDummyClient);
17834  if (result != MA_SUCCESS) {
17835  ma_free(pContext->jack.pClientName);
17836  #ifndef MA_NO_RUNTIME_LINKING
17837  ma_dlclose(pContext, pContext->jack.jackSO);
17838  #endif
17839  return MA_NO_BACKEND;
17840  }
17841 
17842  ((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient);
17843  }
17844 
17845  return MA_SUCCESS;
17846 }
17847 #endif /* JACK */
17848 
17849 
17850 
17851 /******************************************************************************
17852 
17853 Core Audio Backend
17854 
17855 ******************************************************************************/
17856 #ifdef MA_HAS_COREAUDIO
17857 #include <TargetConditionals.h>
17858 
17859 #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1
17860  #define MA_APPLE_MOBILE
17861 #else
17862  #define MA_APPLE_DESKTOP
17863 #endif
17864 
17865 #if defined(MA_APPLE_DESKTOP)
17866 #include <CoreAudio/CoreAudio.h>
17867 #else
17868 #include <AVFoundation/AVFoundation.h>
17869 #endif
17870 
17871 #include <AudioToolbox/AudioToolbox.h>
17872 
17873 /* CoreFoundation */
17874 typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding);
17875 typedef void (* ma_CFRelease_proc)(CFTypeRef cf);
17876 
17877 /* CoreAudio */
17878 #if defined(MA_APPLE_DESKTOP)
17879 typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData);
17880 typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
17881 typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
17882 typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
17883 typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
17884 #endif
17885 
17886 /* AudioToolbox */
17887 typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc);
17888 typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance);
17889 typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance);
17890 typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit);
17891 typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit);
17892 typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData);
17893 typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable);
17894 typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize);
17895 typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
17896 typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit);
17897 typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
17898 
17899 
17900 #define MA_COREAUDIO_OUTPUT_BUS 0
17901 #define MA_COREAUDIO_INPUT_BUS 1
17902 
17903 ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit);
17904 
17905 /*
17906 Core Audio
17907 
17908 So far, Core Audio has been the worst backend to work with due to being both unintuitive and having almost no documentation
17909 apart from comments in the headers (which admittedly are quite good). For my own purposes, and for anybody out there whose
17910 needing to figure out how this darn thing works, I'm going to outline a few things here.
17911 
17912 Since miniaudio is a fairly low-level API, one of the things it needs is control over specific devices, and it needs to be
17913 able to identify whether or not it can be used as playback and/or capture. The AudioObject API is the only one I've seen
17914 that supports this level of detail. There was some public domain sample code I stumbled across that used the AudioComponent
17915 and AudioUnit APIs, but I couldn't see anything that gave low-level control over device selection and capabilities (the
17916 distinction between playback and capture in particular). Therefore, miniaudio is using the AudioObject API.
17917 
17918 Most (all?) functions in the AudioObject API take a AudioObjectID as it's input. This is the device identifier. When
17919 retrieving global information, such as the device list, you use kAudioObjectSystemObject. When retrieving device-specific
17920 data, you pass in the ID for that device. In order to retrieve device-specific IDs you need to enumerate over each of the
17921 devices. This is done using the AudioObjectGetPropertyDataSize() and AudioObjectGetPropertyData() APIs which seem to be
17922 the central APIs for retrieving information about the system and specific devices.
17923 
17924 To use the AudioObjectGetPropertyData() API you need to use the notion of a property address. A property address is a
17925 structure with three variables and is used to identify which property you are getting or setting. The first is the "selector"
17926 which is basically the specific property that you're wanting to retrieve or set. The second is the "scope", which is
17927 typically set to kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyScopeInput for input-specific properties and
17928 kAudioObjectPropertyScopeOutput for output-specific properties. The last is the "element" which is always set to
17929 kAudioObjectPropertyElementMaster in miniaudio's case. I don't know of any cases where this would be set to anything different.
17930 
17931 Back to the earlier issue of device retrieval, you first use the AudioObjectGetPropertyDataSize() API to retrieve the size
17932 of the raw data which is just a list of AudioDeviceID's. You use the kAudioObjectSystemObject AudioObjectID, and a property
17933 address with the kAudioHardwarePropertyDevices selector and the kAudioObjectPropertyScopeGlobal scope. Once you have the
17934 size, allocate a block of memory of that size and then call AudioObjectGetPropertyData(). The data is just a list of
17935 AudioDeviceID's so just do "dataSize/sizeof(AudioDeviceID)" to know the device count.
17936 */
17937 
17938 ma_result ma_result_from_OSStatus(OSStatus status)
17939 {
17940  switch (status)
17941  {
17942  case noErr: return MA_SUCCESS;
17943  #if defined(MA_APPLE_DESKTOP)
17944  case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED;
17945  case kAudioHardwareUnspecifiedError: return MA_ERROR;
17946  case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS;
17947  case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION;
17948  case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION;
17949  case kAudioHardwareBadObjectError: return MA_INVALID_ARGS;
17950  case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS;
17951  case kAudioHardwareBadStreamError: return MA_INVALID_ARGS;
17952  case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION;
17953  case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED;
17954  case kAudioDevicePermissionsError: return MA_ACCESS_DENIED;
17955  #endif
17956  default: return MA_ERROR;
17957  }
17958 }
17959 
17960 #if 0
17961 ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit)
17962 {
17963  switch (bit)
17964  {
17965  case kAudioChannelBit_Left: return MA_CHANNEL_LEFT;
17966  case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT;
17967  case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER;
17968  case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE;
17969  case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT;
17970  case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT;
17971  case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
17972  case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
17973  case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER;
17974  case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
17975  case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
17976  case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
17977  case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
17978  case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
17979  case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
17980  case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
17981  case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
17982  case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
17983  default: return MA_CHANNEL_NONE;
17984  }
17985 }
17986 #endif
17987 
17988 ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label)
17989 {
17990  switch (label)
17991  {
17992  case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE;
17993  case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE;
17994  case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE;
17995  case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT;
17996  case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT;
17997  case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER;
17998  case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE;
17999  case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT;
18000  case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT;
18001  case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
18002  case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
18003  case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER;
18004  case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
18005  case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
18006  case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
18007  case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
18008  case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
18009  case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
18010  case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
18011  case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
18012  case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
18013  case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT;
18014  case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT;
18015  case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT;
18016  case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT;
18017  case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE;
18018  case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT;
18019  case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT;
18020  case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE;
18021  case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO;
18022  case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO;
18023  case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO;
18024  case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER;
18025  case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE;
18026  case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE;
18027  case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE;
18028  case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE;
18029  case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE;
18030  case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT;
18031  case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT;
18032  case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT;
18033  case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT;
18034  case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT;
18035  case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT;
18036  case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE;
18037  case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE;
18038  case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE;
18039  case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0;
18040  case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1;
18041  case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2;
18042  case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3;
18043  case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4;
18044  case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5;
18045  case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6;
18046  case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7;
18047  case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8;
18048  case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9;
18049  case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10;
18050  case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11;
18051  case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12;
18052  case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13;
18053  case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14;
18054  case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15;
18055  case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE;
18056 
18057  #if 0 /* Introduced in a later version of macOS. */
18058  case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE;
18059  case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0;
18060  case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1;
18061  case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2;
18062  case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3;
18063  case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4;
18064  case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5;
18065  case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6;
18066  case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7;
18067  case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8;
18068  case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9;
18069  case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10;
18070  case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11;
18071  case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12;
18072  case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13;
18073  case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14;
18074  case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15;
18075  case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE;
18076  #endif
18077 
18078  default: return MA_CHANNEL_NONE;
18079  }
18080 }
18081 
18082 ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut)
18083 {
18084  ma_assert(pDescription != NULL);
18085  ma_assert(pFormatOut != NULL);
18086 
18087  *pFormatOut = ma_format_unknown; /* Safety. */
18088 
18089  /* There's a few things miniaudio doesn't support. */
18090  if (pDescription->mFormatID != kAudioFormatLinearPCM) {
18091  return MA_FORMAT_NOT_SUPPORTED;
18092  }
18093 
18094  /* We don't support any non-packed formats that are aligned high. */
18095  if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) {
18096  return MA_FORMAT_NOT_SUPPORTED;
18097  }
18098 
18099  /* Only supporting native-endian. */
18100  if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) {
18101  return MA_FORMAT_NOT_SUPPORTED;
18102  }
18103 
18104  /* We are not currently supporting non-interleaved formats (this will be added in a future version of miniaudio). */
18105  /*if ((pDescription->mFormatFlags & kAudioFormatFlagIsNonInterleaved) != 0) {
18106  return MA_FORMAT_NOT_SUPPORTED;
18107  }*/
18108 
18109  if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) {
18110  if (pDescription->mBitsPerChannel == 32) {
18111  *pFormatOut = ma_format_f32;
18112  return MA_SUCCESS;
18113  }
18114  } else {
18115  if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) {
18116  if (pDescription->mBitsPerChannel == 16) {
18117  *pFormatOut = ma_format_s16;
18118  return MA_SUCCESS;
18119  } else if (pDescription->mBitsPerChannel == 24) {
18120  if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) {
18121  *pFormatOut = ma_format_s24;
18122  return MA_SUCCESS;
18123  } else {
18124  if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) {
18125  /* TODO: Implement ma_format_s24_32. */
18127  /*return MA_SUCCESS;*/
18128  return MA_FORMAT_NOT_SUPPORTED;
18129  }
18130  }
18131  } else if (pDescription->mBitsPerChannel == 32) {
18132  *pFormatOut = ma_format_s32;
18133  return MA_SUCCESS;
18134  }
18135  } else {
18136  if (pDescription->mBitsPerChannel == 8) {
18137  *pFormatOut = ma_format_u8;
18138  return MA_SUCCESS;
18139  }
18140  }
18141  }
18142 
18143  /* Getting here means the format is not supported. */
18144  return MA_FORMAT_NOT_SUPPORTED;
18145 }
18146 
18147 ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel channelMap[MA_MAX_CHANNELS])
18148 {
18149  ma_assert(pChannelLayout != NULL);
18150 
18151  if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
18152  UInt32 iChannel;
18153  for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions; ++iChannel) {
18154  channelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
18155  }
18156  } else
18157 #if 0
18158  if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
18159  /* This is the same kind of system that's used by Windows audio APIs. */
18160  UInt32 iChannel = 0;
18161  UInt32 iBit;
18162  AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;
18163  for (iBit = 0; iBit < 32; ++iBit) {
18164  AudioChannelBitmap bit = bitmap & (1 << iBit);
18165  if (bit != 0) {
18166  channelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit);
18167  }
18168  }
18169  } else
18170 #endif
18171  {
18172  /*
18173  Need to use the tag to determine the channel map. For now I'm just assuming a default channel map, but later on this should
18174  be updated to determine the mapping based on the tag.
18175  */
18176  UInt32 channelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
18177  switch (pChannelLayout->mChannelLayoutTag)
18178  {
18179  case kAudioChannelLayoutTag_Mono:
18180  case kAudioChannelLayoutTag_Stereo:
18181  case kAudioChannelLayoutTag_StereoHeadphones:
18182  case kAudioChannelLayoutTag_MatrixStereo:
18183  case kAudioChannelLayoutTag_MidSide:
18184  case kAudioChannelLayoutTag_XY:
18185  case kAudioChannelLayoutTag_Binaural:
18186  case kAudioChannelLayoutTag_Ambisonic_B_Format:
18187  {
18189  } break;
18190 
18191  case kAudioChannelLayoutTag_Octagonal:
18192  {
18193  channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
18194  channelMap[6] = MA_CHANNEL_SIDE_LEFT;
18195  } /* Intentional fallthrough. */
18196  case kAudioChannelLayoutTag_Hexagonal:
18197  {
18198  channelMap[5] = MA_CHANNEL_BACK_CENTER;
18199  } /* Intentional fallthrough. */
18200  case kAudioChannelLayoutTag_Pentagonal:
18201  {
18202  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
18203  } /* Intentional fallghrough. */
18204  case kAudioChannelLayoutTag_Quadraphonic:
18205  {
18206  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
18207  channelMap[2] = MA_CHANNEL_BACK_LEFT;
18208  channelMap[1] = MA_CHANNEL_RIGHT;
18209  channelMap[0] = MA_CHANNEL_LEFT;
18210  } break;
18211 
18212  /* TODO: Add support for more tags here. */
18213 
18214  default:
18215  {
18217  } break;
18218  }
18219  }
18220 
18221  return MA_SUCCESS;
18222 }
18223 
18224 
18225 #if defined(MA_APPLE_DESKTOP)
18226 ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs) /* NOTE: Free the returned buffer with ma_free(). */
18227 {
18228  AudioObjectPropertyAddress propAddressDevices;
18229  UInt32 deviceObjectsDataSize;
18230  OSStatus status;
18231  AudioObjectID* pDeviceObjectIDs;
18232 
18233  ma_assert(pContext != NULL);
18234  ma_assert(pDeviceCount != NULL);
18235  ma_assert(ppDeviceObjectIDs != NULL);
18236 
18237  /* Safety. */
18238  *pDeviceCount = 0;
18239  *ppDeviceObjectIDs = NULL;
18240 
18241  propAddressDevices.mSelector = kAudioHardwarePropertyDevices;
18242  propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal;
18243  propAddressDevices.mElement = kAudioObjectPropertyElementMaster;
18244 
18245  status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize);
18246  if (status != noErr) {
18247  return ma_result_from_OSStatus(status);
18248  }
18249 
18250  pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize);
18251  if (pDeviceObjectIDs == NULL) {
18252  return MA_OUT_OF_MEMORY;
18253  }
18254 
18255  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs);
18256  if (status != noErr) {
18257  ma_free(pDeviceObjectIDs);
18258  return ma_result_from_OSStatus(status);
18259  }
18260 
18261  *pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID);
18262  *ppDeviceObjectIDs = pDeviceObjectIDs;
18263 
18264  (void)pContext; /* Unused. */
18265  return MA_SUCCESS;
18266 }
18267 
18268 ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID)
18269 {
18270  AudioObjectPropertyAddress propAddress;
18271  UInt32 dataSize;
18272  OSStatus status;
18273 
18274  ma_assert(pContext != NULL);
18275 
18276  propAddress.mSelector = kAudioDevicePropertyDeviceUID;
18277  propAddress.mScope = kAudioObjectPropertyScopeGlobal;
18278  propAddress.mElement = kAudioObjectPropertyElementMaster;
18279 
18280  dataSize = sizeof(*pUID);
18281  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID);
18282  if (status != noErr) {
18283  return ma_result_from_OSStatus(status);
18284  }
18285 
18286  return MA_SUCCESS;
18287 }
18288 
18289 ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
18290 {
18291  CFStringRef uid;
18292  ma_result result;
18293 
18294  ma_assert(pContext != NULL);
18295 
18296  result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid);
18297  if (result != MA_SUCCESS) {
18298  return result;
18299  }
18300 
18301  if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
18302  return MA_ERROR;
18303  }
18304 
18305  ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid);
18306  return MA_SUCCESS;
18307 }
18308 
18309 ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
18310 {
18311  AudioObjectPropertyAddress propAddress;
18312  CFStringRef deviceName = NULL;
18313  UInt32 dataSize;
18314  OSStatus status;
18315 
18316  ma_assert(pContext != NULL);
18317 
18318  propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
18319  propAddress.mScope = kAudioObjectPropertyScopeGlobal;
18320  propAddress.mElement = kAudioObjectPropertyElementMaster;
18321 
18322  dataSize = sizeof(deviceName);
18323  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName);
18324  if (status != noErr) {
18325  return ma_result_from_OSStatus(status);
18326  }
18327 
18328  if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
18329  return MA_ERROR;
18330  }
18331 
18332  ((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName);
18333  return MA_SUCCESS;
18334 }
18335 
18336 ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope)
18337 {
18338  AudioObjectPropertyAddress propAddress;
18339  UInt32 dataSize;
18340  OSStatus status;
18341  AudioBufferList* pBufferList;
18342  ma_bool32 isSupported;
18343 
18344  ma_assert(pContext != NULL);
18345 
18346  /* To know whether or not a device is an input device we need ot look at the stream configuration. If it has an output channel it's a playback device. */
18347  propAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
18348  propAddress.mScope = scope;
18349  propAddress.mElement = kAudioObjectPropertyElementMaster;
18350 
18351  status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
18352  if (status != noErr) {
18353  return MA_FALSE;
18354  }
18355 
18356  pBufferList = (AudioBufferList*)ma_malloc(dataSize);
18357  if (pBufferList == NULL) {
18358  return MA_FALSE; /* Out of memory. */
18359  }
18360 
18361  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList);
18362  if (status != noErr) {
18363  ma_free(pBufferList);
18364  return MA_FALSE;
18365  }
18366 
18367  isSupported = MA_FALSE;
18368  if (pBufferList->mNumberBuffers > 0) {
18369  isSupported = MA_TRUE;
18370  }
18371 
18372  ma_free(pBufferList);
18373  return isSupported;
18374 }
18375 
18376 ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID)
18377 {
18378  return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput);
18379 }
18380 
18381 ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID)
18382 {
18383  return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput);
18384 }
18385 
18386 
18387 ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions) /* NOTE: Free the returned pointer with ma_free(). */
18388 {
18389  AudioObjectPropertyAddress propAddress;
18390  UInt32 dataSize;
18391  OSStatus status;
18392  AudioStreamRangedDescription* pDescriptions;
18393 
18394  ma_assert(pContext != NULL);
18395  ma_assert(pDescriptionCount != NULL);
18396  ma_assert(ppDescriptions != NULL);
18397 
18398  /*
18399  TODO: Experiment with kAudioStreamPropertyAvailablePhysicalFormats instead of (or in addition to) kAudioStreamPropertyAvailableVirtualFormats. My
18400  MacBook Pro uses s24/32 format, however, which miniaudio does not currently support.
18401  */
18402  propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats; /*kAudioStreamPropertyAvailablePhysicalFormats;*/
18403  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
18404  propAddress.mElement = kAudioObjectPropertyElementMaster;
18405 
18406  status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
18407  if (status != noErr) {
18408  return ma_result_from_OSStatus(status);
18409  }
18410 
18411  pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize);
18412  if (pDescriptions == NULL) {
18413  return MA_OUT_OF_MEMORY;
18414  }
18415 
18416  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions);
18417  if (status != noErr) {
18418  ma_free(pDescriptions);
18419  return ma_result_from_OSStatus(status);
18420  }
18421 
18422  *pDescriptionCount = dataSize / sizeof(*pDescriptions);
18423  *ppDescriptions = pDescriptions;
18424  return MA_SUCCESS;
18425 }
18426 
18427 
18428 ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, AudioChannelLayout** ppChannelLayout) /* NOTE: Free the returned pointer with ma_free(). */
18429 {
18430  AudioObjectPropertyAddress propAddress;
18431  UInt32 dataSize;
18432  OSStatus status;
18433  AudioChannelLayout* pChannelLayout;
18434 
18435  ma_assert(pContext != NULL);
18436  ma_assert(ppChannelLayout != NULL);
18437 
18438  *ppChannelLayout = NULL; /* Safety. */
18439 
18440  propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
18441  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
18442  propAddress.mElement = kAudioObjectPropertyElementMaster;
18443 
18444  status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
18445  if (status != noErr) {
18446  return ma_result_from_OSStatus(status);
18447  }
18448 
18449  pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize);
18450  if (pChannelLayout == NULL) {
18451  return MA_OUT_OF_MEMORY;
18452  }
18453 
18454  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout);
18455  if (status != noErr) {
18456  ma_free(pChannelLayout);
18457  return ma_result_from_OSStatus(status);
18458  }
18459 
18460  *ppChannelLayout = pChannelLayout;
18461  return MA_SUCCESS;
18462 }
18463 
18464 ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount)
18465 {
18466  AudioChannelLayout* pChannelLayout;
18467  ma_result result;
18468 
18469  ma_assert(pContext != NULL);
18470  ma_assert(pChannelCount != NULL);
18471 
18472  *pChannelCount = 0; /* Safety. */
18473 
18474  result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
18475  if (result != MA_SUCCESS) {
18476  return result;
18477  }
18478 
18479  if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
18480  *pChannelCount = pChannelLayout->mNumberChannelDescriptions;
18481  } else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
18482  *pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap);
18483  } else {
18484  *pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
18485  }
18486 
18487  ma_free(pChannelLayout);
18488  return MA_SUCCESS;
18489 }
18490 
18491 ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel channelMap[MA_MAX_CHANNELS])
18492 {
18493  AudioChannelLayout* pChannelLayout;
18494  ma_result result;
18495 
18496  ma_assert(pContext != NULL);
18497 
18498  result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
18499  if (result != MA_SUCCESS) {
18500  return result; /* Rather than always failing here, would it be more robust to simply assume a default? */
18501  }
18502 
18503  result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap);
18504  if (result != MA_SUCCESS) {
18505  ma_free(pChannelLayout);
18506  return result;
18507  }
18508 
18509  ma_free(pChannelLayout);
18510  return result;
18511 }
18512 
18513 ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges) /* NOTE: Free the returned pointer with ma_free(). */
18514 {
18515  AudioObjectPropertyAddress propAddress;
18516  UInt32 dataSize;
18517  OSStatus status;
18518  AudioValueRange* pSampleRateRanges;
18519 
18520  ma_assert(pContext != NULL);
18521  ma_assert(pSampleRateRangesCount != NULL);
18522  ma_assert(ppSampleRateRanges != NULL);
18523 
18524  /* Safety. */
18525  *pSampleRateRangesCount = 0;
18526  *ppSampleRateRanges = NULL;
18527 
18528  propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
18529  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
18530  propAddress.mElement = kAudioObjectPropertyElementMaster;
18531 
18532  status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
18533  if (status != noErr) {
18534  return ma_result_from_OSStatus(status);
18535  }
18536 
18537  pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize);
18538  if (pSampleRateRanges == NULL) {
18539  return MA_OUT_OF_MEMORY;
18540  }
18541 
18542  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges);
18543  if (status != noErr) {
18544  ma_free(pSampleRateRanges);
18545  return ma_result_from_OSStatus(status);
18546  }
18547 
18548  *pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges);
18549  *ppSampleRateRanges = pSampleRateRanges;
18550  return MA_SUCCESS;
18551 }
18552 
18553 ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut)
18554 {
18555  UInt32 sampleRateRangeCount;
18556  AudioValueRange* pSampleRateRanges;
18557  ma_result result;
18558 
18559  ma_assert(pContext != NULL);
18560  ma_assert(pSampleRateOut != NULL);
18561 
18562  *pSampleRateOut = 0; /* Safety. */
18563 
18564  result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
18565  if (result != MA_SUCCESS) {
18566  return result;
18567  }
18568 
18569  if (sampleRateRangeCount == 0) {
18570  ma_free(pSampleRateRanges);
18571  return MA_ERROR; /* Should never hit this case should we? */
18572  }
18573 
18574  if (sampleRateIn == 0) {
18575  /* Search in order of miniaudio's preferred priority. */
18576  UInt32 iMALSampleRate;
18577  for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) {
18578  ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate];
18579  UInt32 iCASampleRate;
18580  for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) {
18581  AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate];
18582  if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) {
18583  *pSampleRateOut = malSampleRate;
18584  ma_free(pSampleRateRanges);
18585  return MA_SUCCESS;
18586  }
18587  }
18588  }
18589 
18590  /*
18591  If we get here it means none of miniaudio's standard sample rates matched any of the supported sample rates from the device. In this
18592  case we just fall back to the first one reported by Core Audio.
18593  */
18594  ma_assert(sampleRateRangeCount > 0);
18595 
18596  *pSampleRateOut = pSampleRateRanges[0].mMinimum;
18597  ma_free(pSampleRateRanges);
18598  return MA_SUCCESS;
18599  } else {
18600  /* Find the closest match to this sample rate. */
18601  UInt32 currentAbsoluteDifference = INT32_MAX;
18602  UInt32 iCurrentClosestRange = (UInt32)-1;
18603  UInt32 iRange;
18604  for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) {
18605  if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) {
18606  *pSampleRateOut = sampleRateIn;
18607  ma_free(pSampleRateRanges);
18608  return MA_SUCCESS;
18609  } else {
18610  UInt32 absoluteDifference;
18611  if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) {
18612  absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn;
18613  } else {
18614  absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum;
18615  }
18616 
18617  if (currentAbsoluteDifference > absoluteDifference) {
18618  currentAbsoluteDifference = absoluteDifference;
18619  iCurrentClosestRange = iRange;
18620  }
18621  }
18622  }
18623 
18624  ma_assert(iCurrentClosestRange != (UInt32)-1);
18625 
18626  *pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum;
18627  ma_free(pSampleRateRanges);
18628  return MA_SUCCESS;
18629  }
18630 
18631  /* Should never get here, but it would mean we weren't able to find any suitable sample rates. */
18632  /*ma_free(pSampleRateRanges);*/
18633  /*return MA_ERROR;*/
18634 }
18635 
18636 
18637 ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 bufferSizeInFramesIn, ma_uint32* pBufferSizeInFramesOut)
18638 {
18639  AudioObjectPropertyAddress propAddress;
18640  AudioValueRange bufferSizeRange;
18641  UInt32 dataSize;
18642  OSStatus status;
18643 
18644  ma_assert(pContext != NULL);
18645  ma_assert(pBufferSizeInFramesOut != NULL);
18646 
18647  *pBufferSizeInFramesOut = 0; /* Safety. */
18648 
18649  propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
18650  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
18651  propAddress.mElement = kAudioObjectPropertyElementMaster;
18652 
18653  dataSize = sizeof(bufferSizeRange);
18654  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange);
18655  if (status != noErr) {
18656  return ma_result_from_OSStatus(status);
18657  }
18658 
18659  /* This is just a clamp. */
18660  if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) {
18661  *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum;
18662  } else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) {
18663  *pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum;
18664  } else {
18665  *pBufferSizeInFramesOut = bufferSizeInFramesIn;
18666  }
18667 
18668  return MA_SUCCESS;
18669 }
18670 
18671 ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pBufferSizeInOut)
18672 {
18673  ma_result result;
18674  ma_uint32 chosenBufferSizeInFrames;
18675  AudioObjectPropertyAddress propAddress;
18676  UInt32 dataSize;
18677  OSStatus status;
18678 
18679  ma_assert(pContext != NULL);
18680 
18681  result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pBufferSizeInOut, &chosenBufferSizeInFrames);
18682  if (result != MA_SUCCESS) {
18683  return result;
18684  }
18685 
18686  /* Try setting the size of the buffer... If this fails we just use whatever is currently set. */
18687  propAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
18688  propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
18689  propAddress.mElement = kAudioObjectPropertyElementMaster;
18690 
18691  ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames);
18692 
18693  /* Get the actual size of the buffer. */
18694  dataSize = sizeof(*pBufferSizeInOut);
18695  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames);
18696  if (status != noErr) {
18697  return ma_result_from_OSStatus(status);
18698  }
18699 
18700  *pBufferSizeInOut = chosenBufferSizeInFrames;
18701  return MA_SUCCESS;
18702 }
18703 
18704 
18705 ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID)
18706 {
18707  ma_assert(pContext != NULL);
18708  ma_assert(pDeviceObjectID != NULL);
18709 
18710  /* Safety. */
18711  *pDeviceObjectID = 0;
18712 
18713  if (pDeviceID == NULL) {
18714  /* Default device. */
18715  AudioObjectPropertyAddress propAddressDefaultDevice;
18716  UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
18717  AudioObjectID defaultDeviceObjectID;
18718  OSStatus status;
18719 
18720  propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal;
18721  propAddressDefaultDevice.mElement = kAudioObjectPropertyElementMaster;
18722  if (deviceType == ma_device_type_playback) {
18723  propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
18724  } else {
18725  propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice;
18726  }
18727 
18728  defaultDeviceObjectIDSize = sizeof(AudioObjectID);
18729  status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID);
18730  if (status == noErr) {
18731  *pDeviceObjectID = defaultDeviceObjectID;
18732  return MA_SUCCESS;
18733  }
18734  } else {
18735  /* Explicit device. */
18736  UInt32 deviceCount;
18737  AudioObjectID* pDeviceObjectIDs;
18738  ma_result result;
18739  UInt32 iDevice;
18740 
18741  result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
18742  if (result != MA_SUCCESS) {
18743  return result;
18744  }
18745 
18746  for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
18747  AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
18748 
18749  char uid[256];
18750  if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) {
18751  continue;
18752  }
18753 
18754  if (deviceType == ma_device_type_playback) {
18755  if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
18756  if (strcmp(uid, pDeviceID->coreaudio) == 0) {
18757  *pDeviceObjectID = deviceObjectID;
18758  ma_free(pDeviceObjectIDs);
18759  return MA_SUCCESS;
18760  }
18761  }
18762  } else {
18763  if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
18764  if (strcmp(uid, pDeviceID->coreaudio) == 0) {
18765  *pDeviceObjectID = deviceObjectID;
18766  ma_free(pDeviceObjectIDs);
18767  return MA_SUCCESS;
18768  }
18769  }
18770  }
18771  }
18772 
18773  ma_free(pDeviceObjectIDs);
18774  }
18775 
18776  /* If we get here it means we couldn't find the device. */
18777  return MA_NO_DEVICE;
18778 }
18779 
18780 
18781 ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_bool32 usingDefaultFormat, ma_bool32 usingDefaultChannels, ma_bool32 usingDefaultSampleRate, AudioStreamBasicDescription* pFormat)
18782 {
18783  UInt32 deviceFormatDescriptionCount;
18784  AudioStreamRangedDescription* pDeviceFormatDescriptions;
18785  ma_result result;
18786  ma_uint32 desiredSampleRate;
18787  ma_uint32 desiredChannelCount;
18788  ma_format desiredFormat;
18789  AudioStreamBasicDescription bestDeviceFormatSoFar;
18790  ma_bool32 hasSupportedFormat;
18791  UInt32 iFormat;
18792 
18793  result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions);
18794  if (result != MA_SUCCESS) {
18795  return result;
18796  }
18797 
18798  desiredSampleRate = sampleRate;
18799  if (usingDefaultSampleRate) {
18800  /*
18801  When using the device's default sample rate, we get the highest priority standard rate supported by the device. Otherwise
18802  we just use the pre-set rate.
18803  */
18804  ma_uint32 iStandardRate;
18805  for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
18806  ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
18807  ma_bool32 foundRate = MA_FALSE;
18808  UInt32 iDeviceRate;
18809 
18810  for (iDeviceRate = 0; iDeviceRate < deviceFormatDescriptionCount; ++iDeviceRate) {
18811  ma_uint32 deviceRate = (ma_uint32)pDeviceFormatDescriptions[iDeviceRate].mFormat.mSampleRate;
18812 
18813  if (deviceRate == standardRate) {
18814  desiredSampleRate = standardRate;
18815  foundRate = MA_TRUE;
18816  break;
18817  }
18818  }
18819 
18820  if (foundRate) {
18821  break;
18822  }
18823  }
18824  }
18825 
18826  desiredChannelCount = channels;
18827  if (usingDefaultChannels) {
18828  ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &desiredChannelCount); /* <-- Not critical if this fails. */
18829  }
18830 
18831  desiredFormat = format;
18832  if (usingDefaultFormat) {
18833  desiredFormat = g_maFormatPriorities[0];
18834  }
18835 
18836  /*
18837  If we get here it means we don't have an exact match to what the client is asking for. We'll need to find the closest one. The next
18838  loop will check for formats that have the same sample rate to what we're asking for. If there is, we prefer that one in all cases.
18839  */
18840  ma_zero_object(&bestDeviceFormatSoFar);
18841 
18842  hasSupportedFormat = MA_FALSE;
18843  for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
18844  ma_format format;
18845  ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &format);
18846  if (formatResult == MA_SUCCESS && format != ma_format_unknown) {
18847  hasSupportedFormat = MA_TRUE;
18848  bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat;
18849  break;
18850  }
18851  }
18852 
18853  if (!hasSupportedFormat) {
18854  ma_free(pDeviceFormatDescriptions);
18855  return MA_FORMAT_NOT_SUPPORTED;
18856  }
18857 
18858 
18859  for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
18860  AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat;
18861  ma_format thisSampleFormat;
18862  ma_result formatResult;
18863  ma_format bestSampleFormatSoFar;
18864 
18865  /* If the format is not supported by miniaudio we need to skip this one entirely. */
18866  formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat);
18867  if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) {
18868  continue; /* The format is not supported by miniaudio. Skip. */
18869  }
18870 
18871  ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar);
18872 
18873  /* Getting here means the format is supported by miniaudio which makes this format a candidate. */
18874  if (thisDeviceFormat.mSampleRate != desiredSampleRate) {
18875  /*
18876  The sample rate does not match, but this format could still be usable, although it's a very low priority. If the best format
18877  so far has an equal sample rate we can just ignore this one.
18878  */
18879  if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) {
18880  continue; /* The best sample rate so far has the same sample rate as what we requested which means it's still the best so far. Skip this format. */
18881  } else {
18882  /* In this case, neither the best format so far nor this one have the same sample rate. Check the channel count next. */
18883  if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) {
18884  /* This format has a different sample rate _and_ a different channel count. */
18885  if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
18886  continue; /* No change to the best format. */
18887  } else {
18888  /*
18889  Both this format and the best so far have different sample rates and different channel counts. Whichever has the
18890  best format is the new best.
18891  */
18892  if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
18893  bestDeviceFormatSoFar = thisDeviceFormat;
18894  continue;
18895  } else {
18896  continue; /* No change to the best format. */
18897  }
18898  }
18899  } else {
18900  /* This format has a different sample rate but the desired channel count. */
18901  if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
18902  /* Both this format and the best so far have the desired channel count. Whichever has the best format is the new best. */
18903  if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
18904  bestDeviceFormatSoFar = thisDeviceFormat;
18905  continue;
18906  } else {
18907  continue; /* No change to the best format for now. */
18908  }
18909  } else {
18910  /* This format has the desired channel count, but the best so far does not. We have a new best. */
18911  bestDeviceFormatSoFar = thisDeviceFormat;
18912  continue;
18913  }
18914  }
18915  }
18916  } else {
18917  /*
18918  The sample rates match which makes this format a very high priority contender. If the best format so far has a different
18919  sample rate it needs to be replaced with this one.
18920  */
18921  if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) {
18922  bestDeviceFormatSoFar = thisDeviceFormat;
18923  continue;
18924  } else {
18925  /* In this case both this format and the best format so far have the same sample rate. Check the channel count next. */
18926  if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) {
18927  /*
18928  In this case this format has the same channel count as what the client is requesting. If the best format so far has
18929  a different count, this one becomes the new best.
18930  */
18931  if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) {
18932  bestDeviceFormatSoFar = thisDeviceFormat;
18933  continue;
18934  } else {
18935  /* In this case both this format and the best so far have the ideal sample rate and channel count. Check the format. */
18936  if (thisSampleFormat == desiredFormat) {
18937  bestDeviceFormatSoFar = thisDeviceFormat;
18938  break; /* Found the exact match. */
18939  } else {
18940  /* The formats are different. The new best format is the one with the highest priority format according to miniaudio. */
18941  if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
18942  bestDeviceFormatSoFar = thisDeviceFormat;
18943  continue;
18944  } else {
18945  continue; /* No change to the best format for now. */
18946  }
18947  }
18948  }
18949  } else {
18950  /*
18951  In this case the channel count is different to what the client has requested. If the best so far has the same channel
18952  count as the requested count then it remains the best.
18953  */
18954  if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
18955  continue;
18956  } else {
18957  /*
18958  This is the case where both have the same sample rate (good) but different channel counts. Right now both have about
18959  the same priority, but we need to compare the format now.
18960  */
18961  if (thisSampleFormat == bestSampleFormatSoFar) {
18962  if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
18963  bestDeviceFormatSoFar = thisDeviceFormat;
18964  continue;
18965  } else {
18966  continue; /* No change to the best format for now. */
18967  }
18968  }
18969  }
18970  }
18971  }
18972  }
18973  }
18974 
18975  *pFormat = bestDeviceFormatSoFar;
18976 
18977  ma_free(pDeviceFormatDescriptions);
18978  return MA_SUCCESS;
18979 }
18980 #endif
18981 
18982 ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel channelMap[MA_MAX_CHANNELS])
18983 {
18984  AudioUnitScope deviceScope;
18985  AudioUnitElement deviceBus;
18986  UInt32 channelLayoutSize;
18987  OSStatus status;
18988  AudioChannelLayout* pChannelLayout;
18989  ma_result result;
18990 
18991  ma_assert(pContext != NULL);
18992 
18993  if (deviceType == ma_device_type_playback) {
18994  deviceScope = kAudioUnitScope_Output;
18995  deviceBus = MA_COREAUDIO_OUTPUT_BUS;
18996  } else {
18997  deviceScope = kAudioUnitScope_Input;
18998  deviceBus = MA_COREAUDIO_INPUT_BUS;
18999  }
19000 
19001  status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL);
19002  if (status != noErr) {
19003  return ma_result_from_OSStatus(status);
19004  }
19005 
19006  pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize);
19007  if (pChannelLayout == NULL) {
19008  return MA_OUT_OF_MEMORY;
19009  }
19010 
19011  status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize);
19012  if (status != noErr) {
19013  ma_free(pChannelLayout);
19014  return ma_result_from_OSStatus(status);
19015  }
19016 
19017  result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, channelMap);
19018  if (result != MA_SUCCESS) {
19019  ma_free(pChannelLayout);
19020  return result;
19021  }
19022 
19023  ma_free(pChannelLayout);
19024  return MA_SUCCESS;
19025 }
19026 
19027 ma_bool32 ma_context_is_device_id_equal__coreaudio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
19028 {
19029  ma_assert(pContext != NULL);
19030  ma_assert(pID0 != NULL);
19031  ma_assert(pID1 != NULL);
19032  (void)pContext;
19033 
19034  return strcmp(pID0->coreaudio, pID1->coreaudio) == 0;
19035 }
19036 
19037 ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
19038 {
19039 #if defined(MA_APPLE_DESKTOP)
19040  UInt32 deviceCount;
19041  AudioObjectID* pDeviceObjectIDs;
19042  ma_result result;
19043  UInt32 iDevice;
19044 
19045  result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
19046  if (result != MA_SUCCESS) {
19047  return result;
19048  }
19049 
19050  for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
19051  AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
19052  ma_device_info info;
19053 
19054  ma_zero_object(&info);
19055  if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) {
19056  continue;
19057  }
19058  if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) {
19059  continue;
19060  }
19061 
19062  if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
19063  if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
19064  break;
19065  }
19066  }
19067  if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
19068  if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
19069  break;
19070  }
19071  }
19072  }
19073 
19074  ma_free(pDeviceObjectIDs);
19075 #else
19076  /* Only supporting default devices on non-Desktop platforms. */
19077  ma_device_info info;
19078 
19079  ma_zero_object(&info);
19080  ma_strncpy_s(info.name, sizeof(info.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
19081  if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
19082  return MA_SUCCESS;
19083  }
19084 
19085  ma_zero_object(&info);
19086  ma_strncpy_s(info.name, sizeof(info.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
19087  if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
19088  return MA_SUCCESS;
19089  }
19090 #endif
19091 
19092  return MA_SUCCESS;
19093 }
19094 
19095 ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
19096 {
19097  ma_result result;
19098 
19099  ma_assert(pContext != NULL);
19100 
19101  /* No exclusive mode with the Core Audio backend for now. */
19102  if (shareMode == ma_share_mode_exclusive) {
19104  }
19105 
19106 #if defined(MA_APPLE_DESKTOP)
19107  /* Desktop */
19108  {
19109  AudioObjectID deviceObjectID;
19110  UInt32 streamDescriptionCount;
19111  AudioStreamRangedDescription* pStreamDescriptions;
19112  UInt32 iStreamDescription;
19113  UInt32 sampleRateRangeCount;
19114  AudioValueRange* pSampleRateRanges;
19115 
19116  result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
19117  if (result != MA_SUCCESS) {
19118  return result;
19119  }
19120 
19121  result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio);
19122  if (result != MA_SUCCESS) {
19123  return result;
19124  }
19125 
19126  result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name);
19127  if (result != MA_SUCCESS) {
19128  return result;
19129  }
19130 
19131  /* Formats. */
19132  result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
19133  if (result != MA_SUCCESS) {
19134  return result;
19135  }
19136 
19137  for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) {
19138  ma_format format;
19139  ma_bool32 formatExists = MA_FALSE;
19140  ma_uint32 iOutputFormat;
19141 
19142  result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format);
19143  if (result != MA_SUCCESS) {
19144  continue;
19145  }
19146 
19147  ma_assert(format != ma_format_unknown);
19148 
19149  /* Make sure the format isn't already in the output list. */
19150  for (iOutputFormat = 0; iOutputFormat < pDeviceInfo->formatCount; ++iOutputFormat) {
19151  if (pDeviceInfo->formats[iOutputFormat] == format) {
19152  formatExists = MA_TRUE;
19153  break;
19154  }
19155  }
19156 
19157  if (!formatExists) {
19158  pDeviceInfo->formats[pDeviceInfo->formatCount++] = format;
19159  }
19160  }
19161 
19162  ma_free(pStreamDescriptions);
19163 
19164 
19165  /* Channels. */
19166  result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &pDeviceInfo->minChannels);
19167  if (result != MA_SUCCESS) {
19168  return result;
19169  }
19170  pDeviceInfo->maxChannels = pDeviceInfo->minChannels;
19171 
19172 
19173  /* Sample rates. */
19174  result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
19175  if (result != MA_SUCCESS) {
19176  return result;
19177  }
19178 
19179  if (sampleRateRangeCount > 0) {
19180  UInt32 iSampleRate;
19181  pDeviceInfo->minSampleRate = UINT32_MAX;
19182  pDeviceInfo->maxSampleRate = 0;
19183  for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) {
19184  if (pDeviceInfo->minSampleRate > pSampleRateRanges[iSampleRate].mMinimum) {
19185  pDeviceInfo->minSampleRate = pSampleRateRanges[iSampleRate].mMinimum;
19186  }
19187  if (pDeviceInfo->maxSampleRate < pSampleRateRanges[iSampleRate].mMaximum) {
19188  pDeviceInfo->maxSampleRate = pSampleRateRanges[iSampleRate].mMaximum;
19189  }
19190  }
19191  }
19192  }
19193 #else
19194  /* Mobile */
19195  {
19196  AudioComponentDescription desc;
19197  AudioComponent component;
19198  AudioUnit audioUnit;
19199  OSStatus status;
19200  AudioUnitScope formatScope;
19201  AudioUnitElement formatElement;
19202  AudioStreamBasicDescription bestFormat;
19203  UInt32 propSize;
19204 
19205  if (deviceType == ma_device_type_playback) {
19206  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
19207  } else {
19208  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
19209  }
19210 
19211  /*
19212  Retrieving device information is more annoying on mobile than desktop. For simplicity I'm locking this down to whatever format is
19213  reported on a temporary I/O unit. The problem, however, is that this doesn't return a value for the sample rate which we need to
19214  retrieve from the AVAudioSession shared instance.
19215  */
19216  desc.componentType = kAudioUnitType_Output;
19217  desc.componentSubType = kAudioUnitSubType_RemoteIO;
19218  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
19219  desc.componentFlags = 0;
19220  desc.componentFlagsMask = 0;
19221 
19222  component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
19223  if (component == NULL) {
19225  }
19226 
19227  status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit);
19228  if (status != noErr) {
19229  return ma_result_from_OSStatus(status);
19230  }
19231 
19232  formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
19233  formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
19234 
19235  propSize = sizeof(bestFormat);
19236  status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
19237  if (status != noErr) {
19238  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
19239  return ma_result_from_OSStatus(status);
19240  }
19241 
19242  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
19243  audioUnit = NULL;
19244 
19245 
19246  pDeviceInfo->minChannels = bestFormat.mChannelsPerFrame;
19247  pDeviceInfo->maxChannels = bestFormat.mChannelsPerFrame;
19248 
19249  pDeviceInfo->formatCount = 1;
19250  result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->formats[0]);
19251  if (result != MA_SUCCESS) {
19252  return result;
19253  }
19254 
19255  /*
19256  It looks like Apple are wanting to push the whole AVAudioSession thing. Thus, we need to use that to determine device settings. To do
19257  this we just get the shared instance and inspect.
19258  */
19259  @autoreleasepool {
19260  AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
19261  ma_assert(pAudioSession != NULL);
19262 
19263  pDeviceInfo->minSampleRate = (ma_uint32)pAudioSession.sampleRate;
19264  pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
19265  }
19266  }
19267 #endif
19268 
19269  (void)pDeviceInfo; /* Unused. */
19270  return MA_SUCCESS;
19271 }
19272 
19273 
19274 OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList)
19275 {
19276  ma_device* pDevice = (ma_device*)pUserData;
19277  ma_stream_layout layout;
19278 
19279  ma_assert(pDevice != NULL);
19280 
19281 #if defined(MA_DEBUG_OUTPUT)
19282  printf("INFO: Output Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pBufferList->mNumberBuffers);
19283 #endif
19284 
19285  /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */
19287  if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) {
19289  }
19290 
19291  if (layout == ma_stream_layout_interleaved) {
19292  /* For now we can assume everything is interleaved. */
19293  UInt32 iBuffer;
19294  for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
19295  if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) {
19296  ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
19297  if (frameCountForThisBuffer > 0) {
19298  if (pDevice->type == ma_device_type_duplex) {
19299  ma_device__handle_duplex_callback_playback(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData, &pDevice->coreaudio.duplexRB);
19300  } else {
19301  ma_device__read_frames_from_client(pDevice, frameCountForThisBuffer, pBufferList->mBuffers[iBuffer].mData);
19302  }
19303  }
19304 
19305  #if defined(MA_DEBUG_OUTPUT)
19306  printf(" frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
19307  #endif
19308  } else {
19309  /*
19310  This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
19311  not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams. We just
19312  output silence here.
19313  */
19314  ma_zero_memory(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
19315 
19316  #if defined(MA_DEBUG_OUTPUT)
19317  printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pBufferList->mBuffers[iBuffer].mNumberChannels, pBufferList->mBuffers[iBuffer].mDataByteSize);
19318  #endif
19319  }
19320  }
19321  } else {
19322  /* This is the deinterleaved case. We need to update each buffer in groups of internalChannels. This assumes each buffer is the same size. */
19323  ma_uint8 tempBuffer[4096];
19324  UInt32 iBuffer;
19325  for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) {
19326  ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat);
19327  ma_uint32 framesRemaining = frameCountPerBuffer;
19328 
19329  while (framesRemaining > 0) {
19330  void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
19331  ma_uint32 iChannel;
19332  ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
19333  if (framesToRead > framesRemaining) {
19334  framesToRead = framesRemaining;
19335  }
19336 
19337  if (pDevice->type == ma_device_type_duplex) {
19338  ma_device__handle_duplex_callback_playback(pDevice, framesToRead, tempBuffer, &pDevice->coreaudio.duplexRB);
19339  } else {
19340  ma_device__read_frames_from_client(pDevice, framesToRead, tempBuffer);
19341  }
19342 
19343  for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
19344  ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
19345  }
19346 
19347  ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
19348 
19349  framesRemaining -= framesToRead;
19350  }
19351  }
19352  }
19353 
19354  (void)pActionFlags;
19355  (void)pTimeStamp;
19356  (void)busNumber;
19357 
19358  return noErr;
19359 }
19360 
19361 OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList)
19362 {
19363  ma_device* pDevice = (ma_device*)pUserData;
19364  AudioBufferList* pRenderedBufferList;
19365  ma_stream_layout layout;
19366  OSStatus status;
19367 
19368  ma_assert(pDevice != NULL);
19369 
19370  pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
19371  ma_assert(pRenderedBufferList);
19372 
19373  /* We need to check whether or not we are outputting interleaved or non-interleaved samples. The way we do this is slightly different for each type. */
19375  if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) {
19377  }
19378 
19379 #if defined(MA_DEBUG_OUTPUT)
19380  printf("INFO: Input Callback: busNumber=%d, frameCount=%d, mNumberBuffers=%d\n", busNumber, frameCount, pRenderedBufferList->mNumberBuffers);
19381 #endif
19382 
19383  status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);
19384  if (status != noErr) {
19385  #if defined(MA_DEBUG_OUTPUT)
19386  printf(" ERROR: AudioUnitRender() failed with %d\n", status);
19387  #endif
19388  return status;
19389  }
19390 
19391  if (layout == ma_stream_layout_interleaved) {
19392  UInt32 iBuffer;
19393  for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
19394  if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) {
19395  if (pDevice->type == ma_device_type_duplex) {
19396  ma_device__handle_duplex_callback_capture(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData, &pDevice->coreaudio.duplexRB);
19397  } else {
19398  ma_device__send_frames_to_client(pDevice, frameCount, pRenderedBufferList->mBuffers[iBuffer].mData);
19399  }
19400  #if defined(MA_DEBUG_OUTPUT)
19401  printf(" mDataByteSize=%d\n", pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
19402  #endif
19403  } else {
19404  /*
19405  This case is where the number of channels in the output buffer do not match our internal channels. It could mean that it's
19406  not interleaved, in which case we can't handle right now since miniaudio does not yet support non-interleaved streams.
19407  */
19408  ma_uint8 silentBuffer[4096];
19409  ma_uint32 framesRemaining;
19410 
19411  ma_zero_memory(silentBuffer, sizeof(silentBuffer));
19412 
19413  framesRemaining = frameCount;
19414  while (framesRemaining > 0) {
19415  ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
19416  if (framesToSend > framesRemaining) {
19417  framesToSend = framesRemaining;
19418  }
19419 
19420  if (pDevice->type == ma_device_type_duplex) {
19421  ma_device__handle_duplex_callback_capture(pDevice, framesToSend, silentBuffer, &pDevice->coreaudio.duplexRB);
19422  } else {
19423  ma_device__send_frames_to_client(pDevice, framesToSend, silentBuffer);
19424  }
19425 
19426  framesRemaining -= framesToSend;
19427  }
19428 
19429  #if defined(MA_DEBUG_OUTPUT)
19430  printf(" WARNING: Outputting silence. frameCount=%d, mNumberChannels=%d, mDataByteSize=%d\n", frameCount, pRenderedBufferList->mBuffers[iBuffer].mNumberChannels, pRenderedBufferList->mBuffers[iBuffer].mDataByteSize);
19431  #endif
19432  }
19433  }
19434  } else {
19435  /* This is the deinterleaved case. We need to interleave the audio data before sending it to the client. This assumes each buffer is the same size. */
19436  ma_uint8 tempBuffer[4096];
19437  UInt32 iBuffer;
19438  for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) {
19439  ma_uint32 framesRemaining = frameCount;
19440  while (framesRemaining > 0) {
19441  void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
19442  ma_uint32 iChannel;
19443  ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_sample(pDevice->capture.internalFormat);
19444  if (framesToSend > framesRemaining) {
19445  framesToSend = framesRemaining;
19446  }
19447 
19448  for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
19449  ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
19450  }
19451 
19452  ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
19453 
19454  if (pDevice->type == ma_device_type_duplex) {
19455  ma_device__handle_duplex_callback_capture(pDevice, framesToSend, tempBuffer, &pDevice->coreaudio.duplexRB);
19456  } else {
19457  ma_device__send_frames_to_client(pDevice, framesToSend, tempBuffer);
19458  }
19459 
19460  framesRemaining -= framesToSend;
19461  }
19462  }
19463  }
19464 
19465  (void)pActionFlags;
19466  (void)pTimeStamp;
19467  (void)busNumber;
19468  (void)frameCount;
19469  (void)pUnusedBufferList;
19470 
19471  return noErr;
19472 }
19473 
19474 void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element)
19475 {
19476  ma_device* pDevice = (ma_device*)pUserData;
19477  ma_assert(pDevice != NULL);
19478 
19479  /*
19480  There's been a report of a deadlock here when triggered by ma_device_uninit(). It looks like
19481  AudioUnitGetProprty (called below) and AudioComponentInstanceDispose (called in ma_device_uninit)
19482  can try waiting on the same lock. I'm going to try working around this by not calling any Core
19483  Audio APIs in the callback when the device has been stopped or uninitialized.
19484  */
19485  if (ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED || ma_device__get_state(pDevice) == MA_STATE_STOPPING || ma_device__get_state(pDevice) == MA_STATE_STOPPED) {
19486  ma_stop_proc onStop = pDevice->onStop;
19487  if (onStop) {
19488  onStop(pDevice);
19489  }
19490 
19491  ma_event_signal(&pDevice->coreaudio.stopEvent);
19492  } else {
19493  UInt32 isRunning;
19494  UInt32 isRunningSize = sizeof(isRunning);
19495  OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
19496  if (status != noErr) {
19497  return; /* Don't really know what to do in this case... just ignore it, I suppose... */
19498  }
19499 
19500  if (!isRunning) {
19501  ma_stop_proc onStop;
19502 
19503  /*
19504  The stop event is a bit annoying in Core Audio because it will be called when we automatically switch the default device. Some scenarios to consider:
19505 
19506  1) When the device is unplugged, this will be called _before_ the default device change notification.
19507  2) When the device is changed via the default device change notification, this will be called _after_ the switch.
19508 
19509  For case #1, we just check if there's a new default device available. If so, we just ignore the stop event. For case #2 we check a flag.
19510  */
19511  if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) ||
19512  ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) {
19513  /*
19514  It looks like the device is switching through an external event, such as the user unplugging the device or changing the default device
19515  via the operating system's sound settings. If we're re-initializing the device, we just terminate because we want the stopping of the
19516  device to be seamless to the client (we don't want them receiving the onStop event and thinking that the device has stopped when it
19517  hasn't!).
19518  */
19519  if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
19520  ((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
19521  return;
19522  }
19523 
19524  /*
19525  Getting here means the device is not reinitializing which means it may have been unplugged. From what I can see, it looks like Core Audio
19526  will try switching to the new default device seamlessly. We need to somehow find a way to determine whether or not Core Audio will most
19527  likely be successful in switching to the new device.
19528 
19529  TODO: Try to predict if Core Audio will switch devices. If not, the onStop callback needs to be posted.
19530  */
19531  return;
19532  }
19533 
19534  /* Getting here means we need to stop the device. */
19535  onStop = pDevice->onStop;
19536  if (onStop) {
19537  onStop(pDevice);
19538  }
19539  }
19540  }
19541 
19542  (void)propertyID; /* Unused. */
19543 }
19544 
19545 #if defined(MA_APPLE_DESKTOP)
19546 static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0;
19547 static ma_mutex g_DeviceTrackingMutex_CoreAudio;
19548 static ma_device** g_ppTrackedDevices_CoreAudio = NULL;
19549 static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0;
19550 static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0;
19551 
19552 OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData)
19553 {
19554  ma_device_type deviceType;
19555 
19556  /* Not sure if I really need to check this, but it makes me feel better. */
19557  if (addressCount == 0) {
19558  return noErr;
19559  }
19560 
19561  if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) {
19562  deviceType = ma_device_type_playback;
19563  } else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) {
19564  deviceType = ma_device_type_capture;
19565  } else {
19566  return noErr; /* Should never hit this. */
19567  }
19568 
19569  ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
19570  {
19571  ma_uint32 iDevice;
19572  for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
19573  ma_result reinitResult;
19574  ma_device* pDevice;
19575 
19576  pDevice = g_ppTrackedDevices_CoreAudio[iDevice];
19577  if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) {
19578  if (deviceType == ma_device_type_playback) {
19579  pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE;
19580  reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
19581  pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE;
19582  } else {
19583  pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE;
19584  reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
19585  pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE;
19586  }
19587 
19588  if (reinitResult == MA_SUCCESS) {
19589  ma_device__post_init_setup(pDevice, deviceType);
19590 
19591  /* Restart the device if required. If this fails we need to stop the device entirely. */
19592  if (ma_device__get_state(pDevice) == MA_STATE_STARTED) {
19593  OSStatus status;
19594  if (deviceType == ma_device_type_playback) {
19595  status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
19596  if (status != noErr) {
19597  if (pDevice->type == ma_device_type_duplex) {
19598  ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
19599  }
19600  ma_device__set_state(pDevice, MA_STATE_STOPPED);
19601  }
19602  } else if (deviceType == ma_device_type_capture) {
19603  status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
19604  if (status != noErr) {
19605  if (pDevice->type == ma_device_type_duplex) {
19606  ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
19607  }
19608  ma_device__set_state(pDevice, MA_STATE_STOPPED);
19609  }
19610  }
19611  }
19612  }
19613  }
19614  }
19615  }
19616  ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
19617 
19618  (void)objectID; /* Unused. */
19619  return noErr;
19620 }
19621 
19622 static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext)
19623 {
19624  ma_assert(pContext != NULL);
19625 
19626  if (ma_atomic_increment_32(&g_DeviceTrackingInitCounter_CoreAudio) == 1) {
19627  AudioObjectPropertyAddress propAddress;
19628  propAddress.mScope = kAudioObjectPropertyScopeGlobal;
19629  propAddress.mElement = kAudioObjectPropertyElementMaster;
19630 
19631  ma_mutex_init(pContext, &g_DeviceTrackingMutex_CoreAudio);
19632 
19633  propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
19634  ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
19635 
19636  propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
19637  ((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
19638  }
19639 
19640  return MA_SUCCESS;
19641 }
19642 
19643 static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext)
19644 {
19645  ma_assert(pContext != NULL);
19646 
19647  if (ma_atomic_decrement_32(&g_DeviceTrackingInitCounter_CoreAudio) == 0) {
19648  AudioObjectPropertyAddress propAddress;
19649  propAddress.mScope = kAudioObjectPropertyScopeGlobal;
19650  propAddress.mElement = kAudioObjectPropertyElementMaster;
19651 
19652  propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
19653  ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
19654 
19655  propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
19656  ((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
19657 
19658  /* At this point there should be no tracked devices. If so there's an error somewhere. */
19659  ma_assert(g_ppTrackedDevices_CoreAudio == NULL);
19660  ma_assert(g_TrackedDeviceCount_CoreAudio == 0);
19661 
19662  ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio);
19663  }
19664 
19665  return MA_SUCCESS;
19666 }
19667 
19668 static ma_result ma_device__track__coreaudio(ma_device* pDevice)
19669 {
19670  ma_result result;
19671 
19672  ma_assert(pDevice != NULL);
19673 
19674  result = ma_context__init_device_tracking__coreaudio(pDevice->pContext);
19675  if (result != MA_SUCCESS) {
19676  return result;
19677  }
19678 
19679  ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
19680  {
19681  /* Allocate memory if required. */
19682  if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) {
19683  ma_uint32 newCap;
19684  ma_device** ppNewDevices;
19685 
19686  newCap = g_TrackedDeviceCap_CoreAudio * 2;
19687  if (newCap == 0) {
19688  newCap = 1;
19689  }
19690 
19691  ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio) * newCap);
19692  if (ppNewDevices == NULL) {
19693  ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
19694  return MA_OUT_OF_MEMORY;
19695  }
19696 
19697  g_ppTrackedDevices_CoreAudio = ppNewDevices;
19698  g_TrackedDeviceCap_CoreAudio = newCap;
19699  }
19700 
19701  g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice;
19702  g_TrackedDeviceCount_CoreAudio += 1;
19703  }
19704  ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
19705 
19706  return MA_SUCCESS;
19707 }
19708 
19709 static ma_result ma_device__untrack__coreaudio(ma_device* pDevice)
19710 {
19711  ma_result result;
19712 
19713  ma_assert(pDevice != NULL);
19714 
19715  ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
19716  {
19717  ma_uint32 iDevice;
19718  for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
19719  if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) {
19720  /* We've found the device. We now need to remove it from the list. */
19721  ma_uint32 jDevice;
19722  for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) {
19723  g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1];
19724  }
19725 
19726  g_TrackedDeviceCount_CoreAudio -= 1;
19727 
19728  /* If there's nothing else in the list we need to free memory. */
19729  if (g_TrackedDeviceCount_CoreAudio == 0) {
19730  ma_free(g_ppTrackedDevices_CoreAudio);
19731  g_ppTrackedDevices_CoreAudio = NULL;
19732  g_TrackedDeviceCap_CoreAudio = 0;
19733  }
19734 
19735  break;
19736  }
19737  }
19738  }
19739  ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
19740 
19741  result = ma_context__uninit_device_tracking__coreaudio(pDevice->pContext);
19742  if (result != MA_SUCCESS) {
19743  return result;
19744  }
19745 
19746  return MA_SUCCESS;
19747 }
19748 #endif
19749 
19750 void ma_device_uninit__coreaudio(ma_device* pDevice)
19751 {
19752  ma_assert(pDevice != NULL);
19753  ma_assert(ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED);
19754 
19755 #if defined(MA_APPLE_DESKTOP)
19756  /*
19757  Make sure we're no longer tracking the device. It doesn't matter if we call this for a non-default device because it'll
19758  just gracefully ignore it.
19759  */
19760  ma_device__untrack__coreaudio(pDevice);
19761 #endif
19762 
19763  if (pDevice->coreaudio.audioUnitCapture != NULL) {
19764  ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
19765  }
19766  if (pDevice->coreaudio.audioUnitPlayback != NULL) {
19767  ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
19768  }
19769 
19770  if (pDevice->coreaudio.pAudioBufferList) {
19771  ma_free(pDevice->coreaudio.pAudioBufferList);
19772  }
19773 
19774  if (pDevice->type == ma_device_type_duplex) {
19775  ma_pcm_rb_uninit(&pDevice->coreaudio.duplexRB);
19776  }
19777 }
19778 
19779 typedef struct
19780 {
19781  /* Input. */
19782  ma_format formatIn;
19783  ma_uint32 channelsIn;
19784  ma_uint32 sampleRateIn;
19785  ma_channel channelMapIn[MA_MAX_CHANNELS];
19786  ma_uint32 bufferSizeInFramesIn;
19787  ma_uint32 bufferSizeInMillisecondsIn;
19788  ma_uint32 periodsIn;
19789  ma_bool32 usingDefaultFormat;
19790  ma_bool32 usingDefaultChannels;
19791  ma_bool32 usingDefaultSampleRate;
19792  ma_bool32 usingDefaultChannelMap;
19793  ma_share_mode shareMode;
19794  ma_bool32 registerStopEvent;
19795 
19796  /* Output. */
19797 #if defined(MA_APPLE_DESKTOP)
19798  AudioObjectID deviceObjectID;
19799 #endif
19800  AudioComponent component;
19801  AudioUnit audioUnit;
19802  AudioBufferList* pAudioBufferList; /* Only used for input devices. */
19803  ma_format formatOut;
19804  ma_uint32 channelsOut;
19805  ma_uint32 sampleRateOut;
19806  ma_channel channelMapOut[MA_MAX_CHANNELS];
19807  ma_uint32 bufferSizeInFramesOut;
19808  ma_uint32 periodsOut;
19809  char deviceName[256];
19810 } ma_device_init_internal_data__coreaudio;
19811 
19812 ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference) /* <-- pDevice is typed as void* intentionally so as to avoid accidentally referencing it. */
19813 {
19814  ma_result result;
19815  OSStatus status;
19816  UInt32 enableIOFlag;
19817  AudioStreamBasicDescription bestFormat;
19818  ma_uint32 actualBufferSizeInFrames;
19819  AURenderCallbackStruct callbackInfo;
19820 #if defined(MA_APPLE_DESKTOP)
19821  AudioObjectID deviceObjectID;
19822 #endif
19823 
19824  /* This API should only be used for a single device type: playback or capture. No full-duplex mode. */
19825  if (deviceType == ma_device_type_duplex) {
19826  return MA_INVALID_ARGS;
19827  }
19828 
19829  ma_assert(pContext != NULL);
19830  ma_assert(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture);
19831 
19832 #if defined(MA_APPLE_DESKTOP)
19833  pData->deviceObjectID = 0;
19834 #endif
19835  pData->component = NULL;
19836  pData->audioUnit = NULL;
19837  pData->pAudioBufferList = NULL;
19838 
19839 #if defined(MA_APPLE_DESKTOP)
19840  result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
19841  if (result != MA_SUCCESS) {
19842  return result;
19843  }
19844 
19845  pData->deviceObjectID = deviceObjectID;
19846 #endif
19847 
19848  /* Core audio doesn't really use the notion of a period so we can leave this unmodified, but not too over the top. */
19849  pData->periodsOut = pData->periodsIn;
19850  if (pData->periodsOut == 0) {
19851  pData->periodsOut = MA_DEFAULT_PERIODS;
19852  }
19853  if (pData->periodsOut > 16) {
19854  pData->periodsOut = 16;
19855  }
19856 
19857 
19858  /* Audio unit. */
19859  status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit);
19860  if (status != noErr) {
19861  return ma_result_from_OSStatus(status);
19862  }
19863 
19864 
19865  /* The input/output buses need to be explicitly enabled and disabled. We set the flag based on the output unit first, then we just swap it for input. */
19866  enableIOFlag = 1;
19867  if (deviceType == ma_device_type_capture) {
19868  enableIOFlag = 0;
19869  }
19870 
19871  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
19872  if (status != noErr) {
19873  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
19874  return ma_result_from_OSStatus(status);
19875  }
19876 
19877  enableIOFlag = (enableIOFlag == 0) ? 1 : 0;
19878  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
19879  if (status != noErr) {
19880  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
19881  return ma_result_from_OSStatus(status);
19882  }
19883 
19884 
19885  /* Set the device to use with this audio unit. This is only used on desktop since we are using defaults on mobile. */
19886 #if defined(MA_APPLE_DESKTOP)
19887  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS, &deviceObjectID, sizeof(AudioDeviceID));
19888  if (status != noErr) {
19889  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
19890  return ma_result_from_OSStatus(result);
19891  }
19892 #endif
19893 
19894  /*
19895  Format. This is the hardest part of initialization because there's a few variables to take into account.
19896  1) The format must be supported by the device.
19897  2) The format must be supported miniaudio.
19898  3) There's a priority that miniaudio prefers.
19899 
19900  Ideally we would like to use a format that's as close to the hardware as possible so we can get as close to a passthrough as possible. The
19901  most important property is the sample rate. miniaudio can do format conversion for any sample rate and channel count, but cannot do the same
19902  for the sample data format. If the sample data format is not supported by miniaudio it must be ignored completely.
19903 
19904  On mobile platforms this is a bit different. We just force the use of whatever the audio unit's current format is set to.
19905  */
19906  {
19907  AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
19908  AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
19909 
19910  #if defined(MA_APPLE_DESKTOP)
19911  AudioStreamBasicDescription origFormat;
19912  UInt32 origFormatSize;
19913 
19914  result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, pData->usingDefaultFormat, pData->usingDefaultChannels, pData->usingDefaultSampleRate, &bestFormat);
19915  if (result != MA_SUCCESS) {
19916  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
19917  return result;
19918  }
19919 
19920  /* From what I can see, Apple's documentation implies that we should keep the sample rate consistent. */
19921  origFormatSize = sizeof(origFormat);
19922  if (deviceType == ma_device_type_playback) {
19923  status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize);
19924  } else {
19925  status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize);
19926  }
19927 
19928  if (status != noErr) {
19929  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
19930  return result;
19931  }
19932 
19933  bestFormat.mSampleRate = origFormat.mSampleRate;
19934 
19935  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
19936  if (status != noErr) {
19937  /* We failed to set the format, so fall back to the current format of the audio unit. */
19938  bestFormat = origFormat;
19939  }
19940  #else
19941  UInt32 propSize = sizeof(bestFormat);
19942  status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
19943  if (status != noErr) {
19944  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
19945  return ma_result_from_OSStatus(status);
19946  }
19947 
19948  /*
19949  Sample rate is a little different here because for some reason kAudioUnitProperty_StreamFormat returns 0... Oh well. We need to instead try
19950  setting the sample rate to what the user has requested and then just see the results of it. Need to use some Objective-C here for this since
19951  it depends on Apple's AVAudioSession API. To do this we just get the shared AVAudioSession instance and then set it. Note that from what I
19952  can tell, it looks like the sample rate is shared between playback and capture for everything.
19953  */
19954  @autoreleasepool {
19955  AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
19956  ma_assert(pAudioSession != NULL);
19957 
19958  [pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil];
19959  bestFormat.mSampleRate = pAudioSession.sampleRate;
19960  }
19961 
19962  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
19963  if (status != noErr) {
19964  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
19965  return ma_result_from_OSStatus(status);
19966  }
19967  #endif
19968 
19969  result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut);
19970  if (result != MA_SUCCESS) {
19971  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
19972  return result;
19973  }
19974 
19975  if (pData->formatOut == ma_format_unknown) {
19976  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
19977  return MA_FORMAT_NOT_SUPPORTED;
19978  }
19979 
19980  pData->channelsOut = bestFormat.mChannelsPerFrame;
19981  pData->sampleRateOut = bestFormat.mSampleRate;
19982  }
19983 
19984  /*
19985  Internal channel map. This is weird in my testing. If I use the AudioObject to get the
19986  channel map, the channel descriptions are set to "Unknown" for some reason. To work around
19987  this it looks like retrieving it from the AudioUnit will work. However, and this is where
19988  it gets weird, it doesn't seem to work with capture devices, nor at all on iOS... Therefore
19989  I'm going to fall back to a default assumption in these cases.
19990  */
19991 #if defined(MA_APPLE_DESKTOP)
19992  result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut);
19993  if (result != MA_SUCCESS) {
19994  #if 0
19995  /* Try falling back to the channel map from the AudioObject. */
19996  result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut);
19997  if (result != MA_SUCCESS) {
19998  return result;
19999  }
20000  #else
20001  /* Fall back to default assumptions. */
20002  ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
20003  #endif
20004  }
20005 #else
20006  /* TODO: Figure out how to get the channel map using AVAudioSession. */
20007  ma_get_standard_channel_map(ma_standard_channel_map_default, pData->channelsOut, pData->channelMapOut);
20008 #endif
20009 
20010 
20011  /* Buffer size. Not allowing this to be configurable on iOS. */
20012  actualBufferSizeInFrames = pData->bufferSizeInFramesIn;
20013 
20014 #if defined(MA_APPLE_DESKTOP)
20015  if (actualBufferSizeInFrames == 0) {
20016  actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->bufferSizeInMillisecondsIn, pData->sampleRateOut);
20017  }
20018 
20019  actualBufferSizeInFrames = actualBufferSizeInFrames / pData->periodsOut;
20020  result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualBufferSizeInFrames);
20021  if (result != MA_SUCCESS) {
20022  return result;
20023  }
20024 
20025  pData->bufferSizeInFramesOut = actualBufferSizeInFrames * pData->periodsOut;
20026 #else
20027  actualBufferSizeInFrames = 4096;
20028  pData->bufferSizeInFramesOut = actualBufferSizeInFrames;
20029 #endif
20030 
20031 
20032  /*
20033  During testing I discovered that the buffer size can be too big. You'll get an error like this:
20034 
20035  kAudioUnitErr_TooManyFramesToProcess : inFramesToProcess=4096, mMaxFramesPerSlice=512
20036 
20037  Note how inFramesToProcess is smaller than mMaxFramesPerSlice. To fix, we need to set kAudioUnitProperty_MaximumFramesPerSlice to that
20038  of the size of our buffer, or do it the other way around and set our buffer size to the kAudioUnitProperty_MaximumFramesPerSlice.
20039  */
20040  {
20041  /*AudioUnitScope propScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
20042  AudioUnitElement propBus = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
20043 
20044  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, propScope, propBus, &actualBufferSizeInFrames, sizeof(actualBufferSizeInFrames));
20045  if (status != noErr) {
20046  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
20047  return ma_result_from_OSStatus(status);
20048  }*/
20049 
20050  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualBufferSizeInFrames, sizeof(actualBufferSizeInFrames));
20051  if (status != noErr) {
20052  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
20053  return ma_result_from_OSStatus(status);
20054  }
20055  }
20056 
20057  /* We need a buffer list if this is an input device. We render into this in the input callback. */
20058  if (deviceType == ma_device_type_capture) {
20059  ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
20060  size_t allocationSize;
20061  AudioBufferList* pBufferList;
20062 
20063  allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer); /* Subtract sizeof(AudioBuffer) because that part is dynamically sized. */
20064  if (isInterleaved) {
20065  /* Interleaved case. This is the simple case because we just have one buffer. */
20066  allocationSize += sizeof(AudioBuffer) * 1;
20067  allocationSize += actualBufferSizeInFrames * ma_get_bytes_per_frame(pData->formatOut, pData->channelsOut);
20068  } else {
20069  /* Non-interleaved case. This is the more complex case because there's more than one buffer. */
20070  allocationSize += sizeof(AudioBuffer) * pData->channelsOut;
20071  allocationSize += actualBufferSizeInFrames * ma_get_bytes_per_sample(pData->formatOut) * pData->channelsOut;
20072  }
20073 
20074  pBufferList = (AudioBufferList*)ma_malloc(allocationSize);
20075  if (pBufferList == NULL) {
20076  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
20077  return MA_OUT_OF_MEMORY;
20078  }
20079 
20080  if (isInterleaved) {
20081  pBufferList->mNumberBuffers = 1;
20082  pBufferList->mBuffers[0].mNumberChannels = pData->channelsOut;
20083  pBufferList->mBuffers[0].mDataByteSize = actualBufferSizeInFrames * ma_get_bytes_per_frame(pData->formatOut, pData->channelsOut);
20084  pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList);
20085  } else {
20086  ma_uint32 iBuffer;
20087  pBufferList->mNumberBuffers = pData->channelsOut;
20088  for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
20089  pBufferList->mBuffers[iBuffer].mNumberChannels = 1;
20090  pBufferList->mBuffers[iBuffer].mDataByteSize = actualBufferSizeInFrames * ma_get_bytes_per_sample(pData->formatOut);
20091  pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * pData->channelsOut)) + (actualBufferSizeInFrames * ma_get_bytes_per_sample(pData->formatOut) * iBuffer);
20092  }
20093  }
20094 
20095  pData->pAudioBufferList = pBufferList;
20096  }
20097 
20098  /* Callbacks. */
20099  callbackInfo.inputProcRefCon = pDevice_DoNotReference;
20100  if (deviceType == ma_device_type_playback) {
20101  callbackInfo.inputProc = ma_on_output__coreaudio;
20102  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, MA_COREAUDIO_OUTPUT_BUS, &callbackInfo, sizeof(callbackInfo));
20103  if (status != noErr) {
20104  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
20105  return ma_result_from_OSStatus(status);
20106  }
20107  } else {
20108  callbackInfo.inputProc = ma_on_input__coreaudio;
20109  status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, MA_COREAUDIO_INPUT_BUS, &callbackInfo, sizeof(callbackInfo));
20110  if (status != noErr) {
20111  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
20112  return ma_result_from_OSStatus(status);
20113  }
20114  }
20115 
20116  /* We need to listen for stop events. */
20117  if (pData->registerStopEvent) {
20118  status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference);
20119  if (status != noErr) {
20120  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
20121  return ma_result_from_OSStatus(status);
20122  }
20123  }
20124 
20125  /* Initialize the audio unit. */
20126  status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit);
20127  if (status != noErr) {
20128  ma_free(pData->pAudioBufferList);
20129  pData->pAudioBufferList = NULL;
20130  ((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
20131  return ma_result_from_OSStatus(status);
20132  }
20133 
20134  /* Grab the name. */
20135 #if defined(MA_APPLE_DESKTOP)
20136  ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName);
20137 #else
20138  if (deviceType == ma_device_type_playback) {
20139  ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
20140  } else {
20141  ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME);
20142  }
20143 #endif
20144 
20145  return result;
20146 }
20147 
20148 ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit)
20149 {
20150  ma_device_init_internal_data__coreaudio data;
20151  ma_result result;
20152 
20153  /* This should only be called for playback or capture, not duplex. */
20154  if (deviceType == ma_device_type_duplex) {
20155  return MA_INVALID_ARGS;
20156  }
20157 
20158  if (deviceType == ma_device_type_capture) {
20159  data.formatIn = pDevice->capture.format;
20160  data.channelsIn = pDevice->capture.channels;
20161  data.sampleRateIn = pDevice->sampleRate;
20162  ma_copy_memory(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
20163  data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
20164  data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
20165  data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
20166  data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
20167  data.shareMode = pDevice->capture.shareMode;
20168  data.registerStopEvent = MA_TRUE;
20169 
20170  if (disposePreviousAudioUnit) {
20171  ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
20172  ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
20173  }
20174  if (pDevice->coreaudio.pAudioBufferList) {
20175  ma_free(pDevice->coreaudio.pAudioBufferList);
20176  }
20177  } else if (deviceType == ma_device_type_playback) {
20178  data.formatIn = pDevice->playback.format;
20179  data.channelsIn = pDevice->playback.channels;
20180  data.sampleRateIn = pDevice->sampleRate;
20181  ma_copy_memory(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
20182  data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
20183  data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
20184  data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
20185  data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
20186  data.shareMode = pDevice->playback.shareMode;
20187  data.registerStopEvent = (pDevice->type != ma_device_type_duplex);
20188 
20189  if (disposePreviousAudioUnit) {
20190  ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
20191  ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
20192  }
20193  }
20194  data.bufferSizeInFramesIn = pDevice->coreaudio.originalBufferSizeInFrames;
20195  data.bufferSizeInMillisecondsIn = pDevice->coreaudio.originalBufferSizeInMilliseconds;
20196  data.periodsIn = pDevice->coreaudio.originalPeriods;
20197 
20198  /* Need at least 3 periods for duplex. */
20199  if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) {
20200  data.periodsIn = 3;
20201  }
20202 
20203  result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice);
20204  if (result != MA_SUCCESS) {
20205  return result;
20206  }
20207 
20208  if (deviceType == ma_device_type_capture) {
20209  #if defined(MA_APPLE_DESKTOP)
20210  pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
20211  #endif
20212  pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
20213  pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
20214 
20215  pDevice->capture.internalFormat = data.formatOut;
20216  pDevice->capture.internalChannels = data.channelsOut;
20217  pDevice->capture.internalSampleRate = data.sampleRateOut;
20218  ma_copy_memory(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
20219  pDevice->capture.internalBufferSizeInFrames = data.bufferSizeInFramesOut;
20220  pDevice->capture.internalPeriods = data.periodsOut;
20221  } else if (deviceType == ma_device_type_playback) {
20222  #if defined(MA_APPLE_DESKTOP)
20223  pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
20224  #endif
20225  pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
20226 
20227  pDevice->playback.internalFormat = data.formatOut;
20228  pDevice->playback.internalChannels = data.channelsOut;
20229  pDevice->playback.internalSampleRate = data.sampleRateOut;
20230  ma_copy_memory(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
20231  pDevice->playback.internalBufferSizeInFrames = data.bufferSizeInFramesOut;
20232  pDevice->playback.internalPeriods = data.periodsOut;
20233  }
20234 
20235  return MA_SUCCESS;
20236 }
20237 
20238 
20239 ma_result ma_device_init__coreaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
20240 {
20241  ma_result result;
20242 
20243  ma_assert(pContext != NULL);
20244  ma_assert(pConfig != NULL);
20245  ma_assert(pDevice != NULL);
20246 
20247  if (pConfig->deviceType == ma_device_type_loopback) {
20249  }
20250 
20251  /* No exclusive mode with the Core Audio backend for now. */
20255  }
20256 
20257  /* Capture needs to be initialized first. */
20258  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
20259  ma_device_init_internal_data__coreaudio data;
20260  data.formatIn = pConfig->capture.format;
20261  data.channelsIn = pConfig->capture.channels;
20262  data.sampleRateIn = pConfig->sampleRate;
20263  ma_copy_memory(data.channelMapIn, pConfig->capture.channelMap, sizeof(pConfig->capture.channelMap));
20264  data.usingDefaultFormat = pDevice->capture.usingDefaultFormat;
20265  data.usingDefaultChannels = pDevice->capture.usingDefaultChannels;
20266  data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
20267  data.usingDefaultChannelMap = pDevice->capture.usingDefaultChannelMap;
20268  data.shareMode = pConfig->capture.shareMode;
20269  data.bufferSizeInFramesIn = pConfig->bufferSizeInFrames;
20270  data.bufferSizeInMillisecondsIn = pConfig->bufferSizeInMilliseconds;
20271  data.periodsIn = pConfig->periods;
20272  data.registerStopEvent = MA_TRUE;
20273 
20274  /* Need at least 3 periods for duplex. */
20275  if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) {
20276  data.periodsIn = 3;
20277  }
20278 
20279  result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pConfig->capture.pDeviceID, &data, (void*)pDevice);
20280  if (result != MA_SUCCESS) {
20281  return result;
20282  }
20283 
20284  pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL);
20285  #if defined(MA_APPLE_DESKTOP)
20286  pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
20287  #endif
20288  pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
20289  pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
20290 
20291  pDevice->capture.internalFormat = data.formatOut;
20292  pDevice->capture.internalChannels = data.channelsOut;
20293  pDevice->capture.internalSampleRate = data.sampleRateOut;
20294  ma_copy_memory(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
20295  pDevice->capture.internalBufferSizeInFrames = data.bufferSizeInFramesOut;
20296  pDevice->capture.internalPeriods = data.periodsOut;
20297 
20298  #if defined(MA_APPLE_DESKTOP)
20299  /*
20300  If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
20301  switch the device in the background.
20302  */
20303  if (pConfig->capture.pDeviceID == NULL) {
20304  ma_device__track__coreaudio(pDevice);
20305  }
20306  #endif
20307  }
20308 
20309  /* Playback. */
20310  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
20311  ma_device_init_internal_data__coreaudio data;
20312  data.formatIn = pConfig->playback.format;
20313  data.channelsIn = pConfig->playback.channels;
20314  data.sampleRateIn = pConfig->sampleRate;
20315  ma_copy_memory(data.channelMapIn, pConfig->playback.channelMap, sizeof(pConfig->playback.channelMap));
20316  data.usingDefaultFormat = pDevice->playback.usingDefaultFormat;
20317  data.usingDefaultChannels = pDevice->playback.usingDefaultChannels;
20318  data.usingDefaultSampleRate = pDevice->usingDefaultSampleRate;
20319  data.usingDefaultChannelMap = pDevice->playback.usingDefaultChannelMap;
20320  data.shareMode = pConfig->playback.shareMode;
20321 
20322  /* In full-duplex mode we want the playback buffer to be the same size as the capture buffer. */
20323  if (pConfig->deviceType == ma_device_type_duplex) {
20324  data.bufferSizeInFramesIn = pDevice->capture.internalBufferSizeInFrames;
20325  data.periodsIn = pDevice->capture.internalPeriods;
20326  data.registerStopEvent = MA_FALSE;
20327  } else {
20328  data.bufferSizeInFramesIn = pConfig->bufferSizeInFrames;
20329  data.bufferSizeInMillisecondsIn = pConfig->bufferSizeInMilliseconds;
20330  data.periodsIn = pConfig->periods;
20331  data.registerStopEvent = MA_TRUE;
20332  }
20333 
20334  result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pConfig->playback.pDeviceID, &data, (void*)pDevice);
20335  if (result != MA_SUCCESS) {
20336  if (pConfig->deviceType == ma_device_type_duplex) {
20337  ((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
20338  if (pDevice->coreaudio.pAudioBufferList) {
20339  ma_free(pDevice->coreaudio.pAudioBufferList);
20340  }
20341  }
20342  return result;
20343  }
20344 
20345  pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL);
20346  #if defined(MA_APPLE_DESKTOP)
20347  pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
20348  #endif
20349  pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
20350 
20351  pDevice->playback.internalFormat = data.formatOut;
20352  pDevice->playback.internalChannels = data.channelsOut;
20353  pDevice->playback.internalSampleRate = data.sampleRateOut;
20354  ma_copy_memory(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
20355  pDevice->playback.internalBufferSizeInFrames = data.bufferSizeInFramesOut;
20356  pDevice->playback.internalPeriods = data.periodsOut;
20357 
20358  #if defined(MA_APPLE_DESKTOP)
20359  /*
20360  If we are using the default device we'll need to listen for changes to the system's default device so we can seemlessly
20361  switch the device in the background.
20362  */
20363  if (pConfig->playback.pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pConfig->capture.pDeviceID != NULL)) {
20364  ma_device__track__coreaudio(pDevice);
20365  }
20366  #endif
20367  }
20368 
20369  pDevice->coreaudio.originalBufferSizeInFrames = pConfig->bufferSizeInFrames;
20370  pDevice->coreaudio.originalBufferSizeInMilliseconds = pConfig->bufferSizeInMilliseconds;
20371  pDevice->coreaudio.originalPeriods = pConfig->periods;
20372 
20373  /*
20374  When stopping the device, a callback is called on another thread. We need to wait for this callback
20375  before returning from ma_device_stop(). This event is used for this.
20376  */
20377  ma_event_init(pContext, &pDevice->coreaudio.stopEvent);
20378 
20379  /* Need a ring buffer for duplex mode. */
20380  if (pConfig->deviceType == ma_device_type_duplex) {
20381  ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_src(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalBufferSizeInFrames);
20382  ma_result result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->coreaudio.duplexRB);
20383  if (result != MA_SUCCESS) {
20384  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[Core Audio] Failed to initialize ring buffer.", result);
20385  }
20386 
20387  /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */
20388  {
20389  ma_uint32 bufferSizeInFrames = rbSizeInFrames / pDevice->capture.internalPeriods;
20390  void* pBufferData;
20391  ma_pcm_rb_acquire_write(&pDevice->coreaudio.duplexRB, &bufferSizeInFrames, &pBufferData);
20392  {
20393  ma_zero_memory(pBufferData, bufferSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
20394  }
20395  ma_pcm_rb_commit_write(&pDevice->coreaudio.duplexRB, bufferSizeInFrames, pBufferData);
20396  }
20397  }
20398 
20399  return MA_SUCCESS;
20400 }
20401 
20402 
20403 ma_result ma_device_start__coreaudio(ma_device* pDevice)
20404 {
20405  ma_assert(pDevice != NULL);
20406 
20407  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
20408  OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
20409  if (status != noErr) {
20410  return ma_result_from_OSStatus(status);
20411  }
20412  }
20413 
20414  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
20415  OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
20416  if (status != noErr) {
20417  if (pDevice->type == ma_device_type_duplex) {
20418  ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
20419  }
20420  return ma_result_from_OSStatus(status);
20421  }
20422  }
20423 
20424  return MA_SUCCESS;
20425 }
20426 
20427 ma_result ma_device_stop__coreaudio(ma_device* pDevice)
20428 {
20429  ma_assert(pDevice != NULL);
20430 
20431  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
20432  OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
20433  if (status != noErr) {
20434  return ma_result_from_OSStatus(status);
20435  }
20436  }
20437 
20438  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
20439  OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
20440  if (status != noErr) {
20441  return ma_result_from_OSStatus(status);
20442  }
20443  }
20444 
20445  /* We need to wait for the callback to finish before returning. */
20446  ma_event_wait(&pDevice->coreaudio.stopEvent);
20447  return MA_SUCCESS;
20448 }
20449 
20450 
20451 ma_result ma_context_uninit__coreaudio(ma_context* pContext)
20452 {
20453  ma_assert(pContext != NULL);
20454  ma_assert(pContext->backend == ma_backend_coreaudio);
20455 
20456 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
20457  ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
20458  ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
20459  ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
20460 #endif
20461 
20462  (void)pContext;
20463  return MA_SUCCESS;
20464 }
20465 
20466 ma_result ma_context_init__coreaudio(const ma_context_config* pConfig, ma_context* pContext)
20467 {
20468  ma_assert(pContext != NULL);
20469 
20470  (void)pConfig;
20471 
20472 #if defined(MA_APPLE_MOBILE)
20473  @autoreleasepool {
20474  AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
20475  ma_assert(pAudioSession != NULL);
20476 
20477  [pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord error:nil];
20478 
20479  /* By default we want miniaudio to use the speakers instead of the receiver. In the future this may be customizable. */
20480  ma_bool32 useSpeakers = MA_TRUE;
20481  if (useSpeakers) {
20482  [pAudioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
20483  }
20484  }
20485 #endif
20486 
20487 #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
20488  pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation");
20489  if (pContext->coreaudio.hCoreFoundation == NULL) {
20490  return MA_API_NOT_FOUND;
20491  }
20492 
20493  pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString");
20494  pContext->coreaudio.CFRelease = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFRelease");
20495 
20496 
20497  pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio");
20498  if (pContext->coreaudio.hCoreAudio == NULL) {
20499  ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
20500  return MA_API_NOT_FOUND;
20501  }
20502 
20503  pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData");
20504  pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize");
20505  pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData");
20506  pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener");
20507  pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener");
20508 
20509  /*
20510  It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still
20511  defined in AudioUnit, but just in case they decide to remove them from there entirely I'm going to implement a fallback.
20512  The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to
20513  AudioToolbox.
20514  */
20515  pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioUnit.framework/AudioUnit");
20516  if (pContext->coreaudio.hAudioUnit == NULL) {
20517  ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
20518  ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
20519  return MA_API_NOT_FOUND;
20520  }
20521 
20522  if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) {
20523  /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */
20524  ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
20525  pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioToolbox.framework/AudioToolbox");
20526  if (pContext->coreaudio.hAudioUnit == NULL) {
20527  ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
20528  ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
20529  return MA_API_NOT_FOUND;
20530  }
20531  }
20532 
20533  pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext");
20534  pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose");
20535  pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew");
20536  pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart");
20537  pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop");
20538  pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener");
20539  pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo");
20540  pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty");
20541  pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty");
20542  pContext->coreaudio.AudioUnitInitialize = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitInitialize");
20543  pContext->coreaudio.AudioUnitRender = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitRender");
20544 #else
20545  pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString;
20546  pContext->coreaudio.CFRelease = (ma_proc)CFRelease;
20547 
20548  #if defined(MA_APPLE_DESKTOP)
20549  pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData;
20550  pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize;
20551  pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData;
20552  pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener;
20553  pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener;
20554  #endif
20555 
20556  pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext;
20557  pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose;
20558  pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew;
20559  pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart;
20560  pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop;
20561  pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener;
20562  pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo;
20563  pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty;
20564  pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty;
20565  pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize;
20566  pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender;
20567 #endif
20568 
20569  pContext->isBackendAsynchronous = MA_TRUE;
20570 
20571  pContext->onUninit = ma_context_uninit__coreaudio;
20572  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__coreaudio;
20573  pContext->onEnumDevices = ma_context_enumerate_devices__coreaudio;
20574  pContext->onGetDeviceInfo = ma_context_get_device_info__coreaudio;
20575  pContext->onDeviceInit = ma_device_init__coreaudio;
20576  pContext->onDeviceUninit = ma_device_uninit__coreaudio;
20577  pContext->onDeviceStart = ma_device_start__coreaudio;
20578  pContext->onDeviceStop = ma_device_stop__coreaudio;
20579 
20580  /* Audio component. */
20581  {
20582  AudioComponentDescription desc;
20583  desc.componentType = kAudioUnitType_Output;
20584  #if defined(MA_APPLE_DESKTOP)
20585  desc.componentSubType = kAudioUnitSubType_HALOutput;
20586  #else
20587  desc.componentSubType = kAudioUnitSubType_RemoteIO;
20588  #endif
20589  desc.componentManufacturer = kAudioUnitManufacturer_Apple;
20590  desc.componentFlags = 0;
20591  desc.componentFlagsMask = 0;
20592 
20593  pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
20594  if (pContext->coreaudio.component == NULL) {
20595  #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
20596  ma_dlclose(pContext, pContext->coreaudio.hAudioUnit);
20597  ma_dlclose(pContext, pContext->coreaudio.hCoreAudio);
20598  ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation);
20599  #endif
20601  }
20602  }
20603 
20604  return MA_SUCCESS;
20605 }
20606 #endif /* Core Audio */
20607 
20608 
20609 
20610 /******************************************************************************
20611 
20612 sndio Backend
20613 
20614 ******************************************************************************/
20615 #ifdef MA_HAS_SNDIO
20616 #include <fcntl.h>
20617 #include <sys/stat.h>
20618 
20619 /*
20620 Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due
20621 to miniaudio's implementation or if it's some kind of system configuration issue, but basically the default device
20622 just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's
20623 demand for it or if I can get it tested and debugged more thoroughly.
20624 */
20625 #if 0
20626 #if defined(__NetBSD__) || defined(__OpenBSD__)
20627 #include <sys/audioio.h>
20628 #endif
20629 #if defined(__FreeBSD__) || defined(__DragonFly__)
20630 #include <sys/soundcard.h>
20631 #endif
20632 #endif
20633 
20634 #define MA_SIO_DEVANY "default"
20635 #define MA_SIO_PLAY 1
20636 #define MA_SIO_REC 2
20637 #define MA_SIO_NENC 8
20638 #define MA_SIO_NCHAN 8
20639 #define MA_SIO_NRATE 16
20640 #define MA_SIO_NCONF 4
20641 
20642 struct ma_sio_hdl; /* <-- Opaque */
20643 
20644 struct ma_sio_par
20645 {
20646  unsigned int bits;
20647  unsigned int bps;
20648  unsigned int sig;
20649  unsigned int le;
20650  unsigned int msb;
20651  unsigned int rchan;
20652  unsigned int pchan;
20653  unsigned int rate;
20654  unsigned int bufsz;
20655  unsigned int xrun;
20656  unsigned int round;
20657  unsigned int appbufsz;
20658  int __pad[3];
20659  unsigned int __magic;
20660 };
20661 
20662 struct ma_sio_enc
20663 {
20664  unsigned int bits;
20665  unsigned int bps;
20666  unsigned int sig;
20667  unsigned int le;
20668  unsigned int msb;
20669 };
20670 
20671 struct ma_sio_conf
20672 {
20673  unsigned int enc;
20674  unsigned int rchan;
20675  unsigned int pchan;
20676  unsigned int rate;
20677 };
20678 
20679 struct ma_sio_cap
20680 {
20681  struct ma_sio_enc enc[MA_SIO_NENC];
20682  unsigned int rchan[MA_SIO_NCHAN];
20683  unsigned int pchan[MA_SIO_NCHAN];
20684  unsigned int rate[MA_SIO_NRATE];
20685  int __pad[7];
20686  unsigned int nconf;
20687  struct ma_sio_conf confs[MA_SIO_NCONF];
20688 };
20689 
20690 typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int);
20691 typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*);
20692 typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
20693 typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
20694 typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*);
20695 typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t);
20696 typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t);
20697 typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*);
20698 typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*);
20699 typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*);
20700 
20701 ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb)
20702 {
20703  /* We only support native-endian right now. */
20704  if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) {
20705  return ma_format_unknown;
20706  }
20707 
20708  if (bits == 8 && bps == 1 && sig == 0) {
20709  return ma_format_u8;
20710  }
20711  if (bits == 16 && bps == 2 && sig == 1) {
20712  return ma_format_s16;
20713  }
20714  if (bits == 24 && bps == 3 && sig == 1) {
20715  return ma_format_s24;
20716  }
20717  if (bits == 24 && bps == 4 && sig == 1 && msb == 0) {
20718  /*return ma_format_s24_32;*/
20719  }
20720  if (bits == 32 && bps == 4 && sig == 1) {
20721  return ma_format_s32;
20722  }
20723 
20724  return ma_format_unknown;
20725 }
20726 
20727 ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps)
20728 {
20729  ma_format bestFormat;
20730  unsigned int iConfig;
20731 
20732  ma_assert(caps != NULL);
20733 
20734  bestFormat = ma_format_unknown;
20735  for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
20736  unsigned int iEncoding;
20737  for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
20738  unsigned int bits;
20739  unsigned int bps;
20740  unsigned int sig;
20741  unsigned int le;
20742  unsigned int msb;
20743  ma_format format;
20744 
20745  if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
20746  continue;
20747  }
20748 
20749  bits = caps->enc[iEncoding].bits;
20750  bps = caps->enc[iEncoding].bps;
20751  sig = caps->enc[iEncoding].sig;
20752  le = caps->enc[iEncoding].le;
20753  msb = caps->enc[iEncoding].msb;
20754  format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
20755  if (format == ma_format_unknown) {
20756  continue; /* Format not supported. */
20757  }
20758 
20759  if (bestFormat == ma_format_unknown) {
20760  bestFormat = format;
20761  } else {
20762  if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) { /* <-- Lower = better. */
20763  bestFormat = format;
20764  }
20765  }
20766  }
20767  }
20768 
20769  return ma_format_unknown;
20770 }
20771 
20772 ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat)
20773 {
20774  ma_uint32 maxChannels;
20775  unsigned int iConfig;
20776 
20777  ma_assert(caps != NULL);
20778  ma_assert(requiredFormat != ma_format_unknown);
20779 
20780  /* Just pick whatever configuration has the most channels. */
20781  maxChannels = 0;
20782  for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
20783  /* The encoding should be of requiredFormat. */
20784  unsigned int iEncoding;
20785  for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
20786  unsigned int iChannel;
20787  unsigned int bits;
20788  unsigned int bps;
20789  unsigned int sig;
20790  unsigned int le;
20791  unsigned int msb;
20792  ma_format format;
20793 
20794  if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
20795  continue;
20796  }
20797 
20798  bits = caps->enc[iEncoding].bits;
20799  bps = caps->enc[iEncoding].bps;
20800  sig = caps->enc[iEncoding].sig;
20801  le = caps->enc[iEncoding].le;
20802  msb = caps->enc[iEncoding].msb;
20803  format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
20804  if (format != requiredFormat) {
20805  continue;
20806  }
20807 
20808  /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
20809  for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
20810  unsigned int chan = 0;
20811  unsigned int channels;
20812 
20813  if (deviceType == ma_device_type_playback) {
20814  chan = caps->confs[iConfig].pchan;
20815  } else {
20816  chan = caps->confs[iConfig].rchan;
20817  }
20818 
20819  if ((chan & (1UL << iChannel)) == 0) {
20820  continue;
20821  }
20822 
20823  if (deviceType == ma_device_type_playback) {
20824  channels = caps->pchan[iChannel];
20825  } else {
20826  channels = caps->rchan[iChannel];
20827  }
20828 
20829  if (maxChannels < channels) {
20830  maxChannels = channels;
20831  }
20832  }
20833  }
20834  }
20835 
20836  return maxChannels;
20837 }
20838 
20839 ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat, ma_uint32 requiredChannels)
20840 {
20841  ma_uint32 firstSampleRate;
20842  ma_uint32 bestSampleRate;
20843  unsigned int iConfig;
20844 
20845  ma_assert(caps != NULL);
20846  ma_assert(requiredFormat != ma_format_unknown);
20847  ma_assert(requiredChannels > 0);
20848  ma_assert(requiredChannels <= MA_MAX_CHANNELS);
20849 
20850  firstSampleRate = 0; /* <-- If the device does not support a standard rate we'll fall back to the first one that's found. */
20851  bestSampleRate = 0;
20852 
20853  for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
20854  /* The encoding should be of requiredFormat. */
20855  unsigned int iEncoding;
20856  for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
20857  unsigned int iChannel;
20858  unsigned int bits;
20859  unsigned int bps;
20860  unsigned int sig;
20861  unsigned int le;
20862  unsigned int msb;
20863  ma_format format;
20864 
20865  if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
20866  continue;
20867  }
20868 
20869  bits = caps->enc[iEncoding].bits;
20870  bps = caps->enc[iEncoding].bps;
20871  sig = caps->enc[iEncoding].sig;
20872  le = caps->enc[iEncoding].le;
20873  msb = caps->enc[iEncoding].msb;
20874  format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
20875  if (format != requiredFormat) {
20876  continue;
20877  }
20878 
20879  /* Getting here means the format is supported. Iterate over each channel count and grab the biggest one. */
20880  for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
20881  unsigned int chan = 0;
20882  unsigned int channels;
20883  unsigned int iRate;
20884 
20885  if (deviceType == ma_device_type_playback) {
20886  chan = caps->confs[iConfig].pchan;
20887  } else {
20888  chan = caps->confs[iConfig].rchan;
20889  }
20890 
20891  if ((chan & (1UL << iChannel)) == 0) {
20892  continue;
20893  }
20894 
20895  if (deviceType == ma_device_type_playback) {
20896  channels = caps->pchan[iChannel];
20897  } else {
20898  channels = caps->rchan[iChannel];
20899  }
20900 
20901  if (channels != requiredChannels) {
20902  continue;
20903  }
20904 
20905  /* Getting here means we have found a compatible encoding/channel pair. */
20906  for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
20907  ma_uint32 rate = (ma_uint32)caps->rate[iRate];
20908  ma_uint32 ratePriority;
20909 
20910  if (firstSampleRate == 0) {
20911  firstSampleRate = rate;
20912  }
20913 
20914  /* Disregard this rate if it's not a standard one. */
20915  ratePriority = ma_get_standard_sample_rate_priority_index(rate);
20916  if (ratePriority == (ma_uint32)-1) {
20917  continue;
20918  }
20919 
20920  if (ma_get_standard_sample_rate_priority_index(bestSampleRate) > ratePriority) { /* Lower = better. */
20921  bestSampleRate = rate;
20922  }
20923  }
20924  }
20925  }
20926  }
20927 
20928  /* If a standard sample rate was not found just fall back to the first one that was iterated. */
20929  if (bestSampleRate == 0) {
20930  bestSampleRate = firstSampleRate;
20931  }
20932 
20933  return bestSampleRate;
20934 }
20935 
20936 
20937 ma_bool32 ma_context_is_device_id_equal__sndio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
20938 {
20939  ma_assert(pContext != NULL);
20940  ma_assert(pID0 != NULL);
20941  ma_assert(pID1 != NULL);
20942  (void)pContext;
20943 
20944  return ma_strcmp(pID0->sndio, pID1->sndio) == 0;
20945 }
20946 
20947 ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
20948 {
20949  ma_bool32 isTerminating = MA_FALSE;
20950  struct ma_sio_hdl* handle;
20951 
20952  ma_assert(pContext != NULL);
20953  ma_assert(callback != NULL);
20954 
20955  /* sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating over default devices for now. */
20956 
20957  /* Playback. */
20958  if (!isTerminating) {
20959  handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0);
20960  if (handle != NULL) {
20961  /* Supports playback. */
20962  ma_device_info deviceInfo;
20963  ma_zero_object(&deviceInfo);
20964  ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY);
20965  ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
20966 
20967  isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
20968 
20969  ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
20970  }
20971  }
20972 
20973  /* Capture. */
20974  if (!isTerminating) {
20975  handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0);
20976  if (handle != NULL) {
20977  /* Supports capture. */
20978  ma_device_info deviceInfo;
20979  ma_zero_object(&deviceInfo);
20980  ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default");
20981  ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME);
20982 
20983  isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
20984 
20985  ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
20986  }
20987  }
20988 
20989  return MA_SUCCESS;
20990 }
20991 
20992 ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
20993 {
20994  char devid[256];
20995  struct ma_sio_hdl* handle;
20996  struct ma_sio_cap caps;
20997  unsigned int iConfig;
20998 
20999  ma_assert(pContext != NULL);
21000  (void)shareMode;
21001 
21002  /* We need to open the device before we can get information about it. */
21003  if (pDeviceID == NULL) {
21004  ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY);
21005  ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME);
21006  } else {
21007  ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio);
21008  ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid);
21009  }
21010 
21011  handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0);
21012  if (handle == NULL) {
21013  return MA_NO_DEVICE;
21014  }
21015 
21016  if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) {
21017  return MA_ERROR;
21018  }
21019 
21020  for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) {
21021  /*
21022  The main thing we care about is that the encoding is supported by miniaudio. If it is, we want to give
21023  preference to some formats over others.
21024  */
21025  unsigned int iEncoding;
21026  unsigned int iChannel;
21027  unsigned int iRate;
21028 
21029  for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
21030  unsigned int bits;
21031  unsigned int bps;
21032  unsigned int sig;
21033  unsigned int le;
21034  unsigned int msb;
21035  ma_format format;
21036  ma_bool32 formatExists = MA_FALSE;
21037  ma_uint32 iExistingFormat;
21038 
21039  if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) {
21040  continue;
21041  }
21042 
21043  bits = caps.enc[iEncoding].bits;
21044  bps = caps.enc[iEncoding].bps;
21045  sig = caps.enc[iEncoding].sig;
21046  le = caps.enc[iEncoding].le;
21047  msb = caps.enc[iEncoding].msb;
21048  format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
21049  if (format == ma_format_unknown) {
21050  continue; /* Format not supported. */
21051  }
21052 
21053  /* Add this format if it doesn't already exist. */
21054  for (iExistingFormat = 0; iExistingFormat < pDeviceInfo->formatCount; iExistingFormat += 1) {
21055  if (pDeviceInfo->formats[iExistingFormat] == format) {
21056  formatExists = MA_TRUE;
21057  break;
21058  }
21059  }
21060 
21061  if (!formatExists) {
21062  pDeviceInfo->formats[pDeviceInfo->formatCount++] = format;
21063  }
21064  }
21065 
21066  /* Channels. */
21067  for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
21068  unsigned int chan = 0;
21069  unsigned int channels;
21070 
21071  if (deviceType == ma_device_type_playback) {
21072  chan = caps.confs[iConfig].pchan;
21073  } else {
21074  chan = caps.confs[iConfig].rchan;
21075  }
21076 
21077  if ((chan & (1UL << iChannel)) == 0) {
21078  continue;
21079  }
21080 
21081  if (deviceType == ma_device_type_playback) {
21082  channels = caps.pchan[iChannel];
21083  } else {
21084  channels = caps.rchan[iChannel];
21085  }
21086 
21087  if (pDeviceInfo->minChannels > channels) {
21088  pDeviceInfo->minChannels = channels;
21089  }
21090  if (pDeviceInfo->maxChannels < channels) {
21091  pDeviceInfo->maxChannels = channels;
21092  }
21093  }
21094 
21095  /* Sample rates. */
21096  for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
21097  if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) {
21098  unsigned int rate = caps.rate[iRate];
21099  if (pDeviceInfo->minSampleRate > rate) {
21100  pDeviceInfo->minSampleRate = rate;
21101  }
21102  if (pDeviceInfo->maxSampleRate < rate) {
21103  pDeviceInfo->maxSampleRate = rate;
21104  }
21105  }
21106  }
21107  }
21108 
21109  ((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
21110  return MA_SUCCESS;
21111 }
21112 
21113 void ma_device_uninit__sndio(ma_device* pDevice)
21114 {
21115  ma_assert(pDevice != NULL);
21116 
21117  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
21118  ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
21119  }
21120 
21121  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
21122  ((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
21123  }
21124 }
21125 
21126 ma_result ma_device_init_handle__sndio(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
21127 {
21128  const char* pDeviceName;
21129  ma_ptr handle;
21130  int openFlags = 0;
21131  struct ma_sio_cap caps;
21132  struct ma_sio_par par;
21133  ma_device_id* pDeviceID;
21134  ma_format format;
21135  ma_uint32 channels;
21136  ma_uint32 sampleRate;
21137  ma_format internalFormat;
21138  ma_uint32 internalChannels;
21139  ma_uint32 internalSampleRate;
21140  ma_uint32 internalBufferSizeInFrames;
21141  ma_uint32 internalPeriods;
21142 
21143  ma_assert(pContext != NULL);
21144  ma_assert(pConfig != NULL);
21145  ma_assert(deviceType != ma_device_type_duplex);
21146  ma_assert(pDevice != NULL);
21147 
21148  if (deviceType == ma_device_type_capture) {
21149  openFlags = MA_SIO_REC;
21150  pDeviceID = pConfig->capture.pDeviceID;
21151  format = pConfig->capture.format;
21152  channels = pConfig->capture.channels;
21153  sampleRate = pConfig->sampleRate;
21154  } else {
21155  openFlags = MA_SIO_PLAY;
21156  pDeviceID = pConfig->playback.pDeviceID;
21157  format = pConfig->playback.format;
21158  channels = pConfig->playback.channels;
21159  sampleRate = pConfig->sampleRate;
21160  }
21161 
21162  pDeviceName = MA_SIO_DEVANY;
21163  if (pDeviceID != NULL) {
21164  pDeviceName = pDeviceID->sndio;
21165  }
21166 
21167  handle = (ma_ptr)((ma_sio_open_proc)pContext->sndio.sio_open)(pDeviceName, openFlags, 0);
21168  if (handle == NULL) {
21169  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
21170  }
21171 
21172  /* We need to retrieve the device caps to determine the most appropriate format to use. */
21173  if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) {
21174  ((ma_sio_close_proc)pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
21175  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps.", MA_ERROR);
21176  }
21177 
21178  /*
21179  Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real
21180  way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this
21181  to the requested channels, regardless of whether or not the default channel count is requested.
21182 
21183  For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the
21184  value returned by ma_find_best_channels_from_sio_cap__sndio().
21185  */
21186  if (deviceType == ma_device_type_capture) {
21187  if (pDevice->capture.usingDefaultFormat) {
21188  format = ma_find_best_format_from_sio_cap__sndio(&caps);
21189  }
21190  if (pDevice->capture.usingDefaultChannels) {
21191  if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
21192  channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
21193  }
21194  }
21195  } else {
21196  if (pDevice->playback.usingDefaultFormat) {
21197  format = ma_find_best_format_from_sio_cap__sndio(&caps);
21198  }
21199  if (pDevice->playback.usingDefaultChannels) {
21200  if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
21201  channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
21202  }
21203  }
21204  }
21205 
21206  if (pDevice->usingDefaultSampleRate) {
21207  sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels);
21208  }
21209 
21210 
21211  ((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par);
21212  par.msb = 0;
21213  par.le = ma_is_little_endian();
21214 
21215  switch (format) {
21216  case ma_format_u8:
21217  {
21218  par.bits = 8;
21219  par.bps = 1;
21220  par.sig = 0;
21221  } break;
21222 
21223  case ma_format_s24:
21224  {
21225  par.bits = 24;
21226  par.bps = 3;
21227  par.sig = 1;
21228  } break;
21229 
21230  case ma_format_s32:
21231  {
21232  par.bits = 32;
21233  par.bps = 4;
21234  par.sig = 1;
21235  } break;
21236 
21237  case ma_format_s16:
21238  case ma_format_f32:
21239  default:
21240  {
21241  par.bits = 16;
21242  par.bps = 2;
21243  par.sig = 1;
21244  } break;
21245  }
21246 
21247  if (deviceType == ma_device_type_capture) {
21248  par.rchan = channels;
21249  } else {
21250  par.pchan = channels;
21251  }
21252 
21253  par.rate = sampleRate;
21254 
21255  internalBufferSizeInFrames = pConfig->bufferSizeInFrames;
21256  if (internalBufferSizeInFrames == 0) {
21257  internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, par.rate);
21258  }
21259 
21260  par.round = internalBufferSizeInFrames / pConfig->periods;
21261  par.appbufsz = par.round * pConfig->periods;
21262 
21263  if (((ma_sio_setpar_proc)pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) {
21264  ((ma_sio_close_proc)pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
21265  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.", MA_FORMAT_NOT_SUPPORTED);
21266  }
21267  if (((ma_sio_getpar_proc)pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) {
21268  ((ma_sio_close_proc)pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
21269  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.", MA_FORMAT_NOT_SUPPORTED);
21270  }
21271 
21272  internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb);
21273  internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan;
21274  internalSampleRate = par.rate;
21275  internalPeriods = par.appbufsz / par.round;
21276  internalBufferSizeInFrames = par.appbufsz;
21277 
21278  if (deviceType == ma_device_type_capture) {
21279  pDevice->sndio.handleCapture = handle;
21280  pDevice->capture.internalFormat = internalFormat;
21281  pDevice->capture.internalChannels = internalChannels;
21282  pDevice->capture.internalSampleRate = internalSampleRate;
21283  ma_get_standard_channel_map(ma_standard_channel_map_sndio, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
21284  pDevice->capture.internalBufferSizeInFrames = internalBufferSizeInFrames;
21285  pDevice->capture.internalPeriods = internalPeriods;
21286  } else {
21287  pDevice->sndio.handlePlayback = handle;
21288  pDevice->playback.internalFormat = internalFormat;
21289  pDevice->playback.internalChannels = internalChannels;
21290  pDevice->playback.internalSampleRate = internalSampleRate;
21291  ma_get_standard_channel_map(ma_standard_channel_map_sndio, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
21292  pDevice->playback.internalBufferSizeInFrames = internalBufferSizeInFrames;
21293  pDevice->playback.internalPeriods = internalPeriods;
21294  }
21295 
21296 #ifdef MA_DEBUG_OUTPUT
21297  printf("DEVICE INFO\n");
21298  printf(" Format: %s\n", ma_get_format_name(internalFormat));
21299  printf(" Channels: %d\n", internalChannels);
21300  printf(" Sample Rate: %d\n", internalSampleRate);
21301  printf(" Buffer Size: %d\n", internalBufferSizeInFrames);
21302  printf(" Periods: %d\n", internalPeriods);
21303  printf(" appbufsz: %d\n", par.appbufsz);
21304  printf(" round: %d\n", par.round);
21305 #endif
21306 
21307  return MA_SUCCESS;
21308 }
21309 
21310 ma_result ma_device_init__sndio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
21311 {
21312  ma_assert(pDevice != NULL);
21313 
21314  ma_zero_object(&pDevice->sndio);
21315 
21316  if (pConfig->deviceType == ma_device_type_loopback) {
21318  }
21319 
21320  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
21321  ma_result result = ma_device_init_handle__sndio(pContext, pConfig, ma_device_type_capture, pDevice);
21322  if (result != MA_SUCCESS) {
21323  return result;
21324  }
21325  }
21326 
21327  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
21328  ma_result result = ma_device_init_handle__sndio(pContext, pConfig, ma_device_type_playback, pDevice);
21329  if (result != MA_SUCCESS) {
21330  return result;
21331  }
21332  }
21333 
21334  return MA_SUCCESS;
21335 }
21336 
21337 ma_result ma_device_stop__sndio(ma_device* pDevice)
21338 {
21339  ma_assert(pDevice != NULL);
21340 
21341  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
21342  ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
21343  }
21344 
21345  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
21346  ((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
21347  }
21348 
21349  return MA_SUCCESS;
21350 }
21351 
21352 ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
21353 {
21354  int result;
21355 
21356  if (pFramesWritten != NULL) {
21357  *pFramesWritten = 0;
21358  }
21359 
21360  result = ((ma_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
21361  if (result == 0) {
21362  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
21363  }
21364 
21365  if (pFramesWritten != NULL) {
21366  *pFramesWritten = frameCount;
21367  }
21368 
21369  return MA_SUCCESS;
21370 }
21371 
21372 ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
21373 {
21374  int result;
21375 
21376  if (pFramesRead != NULL) {
21377  *pFramesRead = 0;
21378  }
21379 
21380  result = ((ma_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct ma_sio_hdl*)pDevice->sndio.handleCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
21381  if (result == 0) {
21382  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
21383  }
21384 
21385  if (pFramesRead != NULL) {
21386  *pFramesRead = frameCount;
21387  }
21388 
21389  return MA_SUCCESS;
21390 }
21391 
21392 ma_result ma_device_main_loop__sndio(ma_device* pDevice)
21393 {
21395  ma_bool32 exitLoop = MA_FALSE;
21396 
21397  /* Devices need to be started here. */
21398  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
21399  ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
21400  }
21401  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
21402  ((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback); /* <-- Doesn't actually playback until data is written. */
21403  }
21404 
21405  while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
21406  switch (pDevice->type)
21407  {
21408  case ma_device_type_duplex:
21409  {
21410  /* The process is: device_read -> convert -> callback -> convert -> device_write */
21411  ma_uint8 capturedDeviceData[8192];
21412  ma_uint8 playbackDeviceData[8192];
21413  ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
21414  ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
21415 
21416  ma_uint32 totalFramesProcessed = 0;
21417  ma_uint32 periodSizeInFrames = ma_min(pDevice->capture.internalBufferSizeInFrames/pDevice->capture.internalPeriods, pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods);
21418 
21419  while (totalFramesProcessed < periodSizeInFrames) {
21420  ma_uint32 framesRemaining = periodSizeInFrames - totalFramesProcessed;
21421  ma_uint32 framesProcessed;
21422  ma_uint32 framesToProcess = framesRemaining;
21423  if (framesToProcess > capturedDeviceDataCapInFrames) {
21424  framesToProcess = capturedDeviceDataCapInFrames;
21425  }
21426 
21427  result = ma_device_read__sndio(pDevice, capturedDeviceData, framesToProcess, &framesProcessed);
21428  if (result != MA_SUCCESS) {
21429  exitLoop = MA_TRUE;
21430  break;
21431  }
21432 
21433  pDevice->capture._dspFrameCount = framesToProcess;
21434  pDevice->capture._dspFrames = capturedDeviceData;
21435 
21436  for (;;) {
21437  ma_uint8 capturedData[8192];
21438  ma_uint8 playbackData[8192];
21439  ma_uint32 capturedDataCapInFrames = sizeof(capturedData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
21440  ma_uint32 playbackDataCapInFrames = sizeof(playbackData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
21441 
21442  ma_uint32 capturedFramesToTryProcessing = ma_min(capturedDataCapInFrames, playbackDataCapInFrames);
21443  ma_uint32 capturedFramesToProcess = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, capturedData, capturedFramesToTryProcessing);
21444  if (capturedFramesToProcess == 0) {
21445  break; /* Don't fire the data callback with zero frames. */
21446  }
21447 
21448  ma_device__on_data(pDevice, playbackData, capturedData, capturedFramesToProcess);
21449 
21450  /* At this point the playbackData buffer should be holding data that needs to be written to the device. */
21451  pDevice->playback._dspFrameCount = capturedFramesToProcess;
21452  pDevice->playback._dspFrames = playbackData;
21453  for (;;) {
21454  ma_uint32 playbackDeviceFramesCount = (ma_uint32)ma_pcm_converter_read(&pDevice->playback.converter, playbackDeviceData, playbackDeviceDataCapInFrames);
21455  if (playbackDeviceFramesCount == 0) {
21456  break;
21457  }
21458 
21459  result = ma_device_write__sndio(pDevice, playbackDeviceData, playbackDeviceFramesCount, NULL);
21460  if (result != MA_SUCCESS) {
21461  exitLoop = MA_TRUE;
21462  break;
21463  }
21464 
21465  if (playbackDeviceFramesCount < playbackDeviceDataCapInFrames) {
21466  break;
21467  }
21468  }
21469 
21470  if (capturedFramesToProcess < capturedFramesToTryProcessing) {
21471  break;
21472  }
21473 
21474  /* In case an error happened from ma_device_write2__alsa()... */
21475  if (result != MA_SUCCESS) {
21476  exitLoop = MA_TRUE;
21477  break;
21478  }
21479  }
21480 
21481  totalFramesProcessed += framesProcessed;
21482  }
21483  } break;
21484 
21486  {
21487  /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
21488  ma_uint8 intermediaryBuffer[8192];
21489  ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
21490  ma_uint32 periodSizeInFrames = pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods;
21491  ma_uint32 framesReadThisPeriod = 0;
21492  while (framesReadThisPeriod < periodSizeInFrames) {
21493  ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
21494  ma_uint32 framesProcessed;
21495  ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
21496  if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
21497  framesToReadThisIteration = intermediaryBufferSizeInFrames;
21498  }
21499 
21500  result = ma_device_read__sndio(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
21501  if (result != MA_SUCCESS) {
21502  exitLoop = MA_TRUE;
21503  break;
21504  }
21505 
21506  ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
21507 
21508  framesReadThisPeriod += framesProcessed;
21509  }
21510  } break;
21511 
21513  {
21514  /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
21515  ma_uint8 intermediaryBuffer[8192];
21516  ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
21517  ma_uint32 periodSizeInFrames = pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods;
21518  ma_uint32 framesWrittenThisPeriod = 0;
21519  while (framesWrittenThisPeriod < periodSizeInFrames) {
21520  ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
21521  ma_uint32 framesProcessed;
21522  ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
21523  if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
21524  framesToWriteThisIteration = intermediaryBufferSizeInFrames;
21525  }
21526 
21527  ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
21528 
21529  result = ma_device_write__sndio(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
21530  if (result != MA_SUCCESS) {
21531  exitLoop = MA_TRUE;
21532  break;
21533  }
21534 
21535  framesWrittenThisPeriod += framesProcessed;
21536  }
21537  } break;
21538 
21539  /* To silence a warning. Will never hit this. */
21541  default: break;
21542  }
21543  }
21544 
21545 
21546  /* Here is where the device is stopped. */
21547  ma_device_stop__sndio(pDevice);
21548 
21549  return result;
21550 }
21551 
21552 ma_result ma_context_uninit__sndio(ma_context* pContext)
21553 {
21554  ma_assert(pContext != NULL);
21555  ma_assert(pContext->backend == ma_backend_sndio);
21556 
21557  (void)pContext;
21558  return MA_SUCCESS;
21559 }
21560 
21561 ma_result ma_context_init__sndio(const ma_context_config* pConfig, ma_context* pContext)
21562 {
21563 #ifndef MA_NO_RUNTIME_LINKING
21564  const char* libsndioNames[] = {
21565  "libsndio.so"
21566  };
21567  size_t i;
21568 
21569  for (i = 0; i < ma_countof(libsndioNames); ++i) {
21570  pContext->sndio.sndioSO = ma_dlopen(pContext, libsndioNames[i]);
21571  if (pContext->sndio.sndioSO != NULL) {
21572  break;
21573  }
21574  }
21575 
21576  if (pContext->sndio.sndioSO == NULL) {
21577  return MA_NO_BACKEND;
21578  }
21579 
21580  pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_open");
21581  pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_close");
21582  pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_setpar");
21583  pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getpar");
21584  pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getcap");
21585  pContext->sndio.sio_write = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_write");
21586  pContext->sndio.sio_read = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_read");
21587  pContext->sndio.sio_start = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_start");
21588  pContext->sndio.sio_stop = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_stop");
21589  pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_initpar");
21590 #else
21591  pContext->sndio.sio_open = sio_open;
21592  pContext->sndio.sio_close = sio_close;
21593  pContext->sndio.sio_setpar = sio_setpar;
21594  pContext->sndio.sio_getpar = sio_getpar;
21595  pContext->sndio.sio_getcap = sio_getcap;
21596  pContext->sndio.sio_write = sio_write;
21597  pContext->sndio.sio_read = sio_read;
21598  pContext->sndio.sio_start = sio_start;
21599  pContext->sndio.sio_stop = sio_stop;
21600  pContext->sndio.sio_initpar = sio_initpar;
21601 #endif
21602 
21603  pContext->onUninit = ma_context_uninit__sndio;
21604  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__sndio;
21605  pContext->onEnumDevices = ma_context_enumerate_devices__sndio;
21606  pContext->onGetDeviceInfo = ma_context_get_device_info__sndio;
21607  pContext->onDeviceInit = ma_device_init__sndio;
21608  pContext->onDeviceUninit = ma_device_uninit__sndio;
21609  pContext->onDeviceStart = NULL; /* Not required for synchronous backends. */
21610  pContext->onDeviceStop = NULL; /* Not required for synchronous backends. */
21611  pContext->onDeviceMainLoop = ma_device_main_loop__sndio;
21612 
21613  (void)pConfig;
21614  return MA_SUCCESS;
21615 }
21616 #endif /* sndio */
21617 
21618 
21619 
21620 /******************************************************************************
21621 
21622 audio(4) Backend
21623 
21624 ******************************************************************************/
21625 #ifdef MA_HAS_AUDIO4
21626 #include <fcntl.h>
21627 #include <poll.h>
21628 #include <errno.h>
21629 #include <sys/stat.h>
21630 #include <sys/types.h>
21631 #include <sys/ioctl.h>
21632 #include <sys/audioio.h>
21633 
21634 #if defined(__OpenBSD__)
21635  #include <sys/param.h>
21636  #if defined(OpenBSD) && OpenBSD >= 201709
21637  #define MA_AUDIO4_USE_NEW_API
21638  #endif
21639 #endif
21640 
21641 void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex)
21642 {
21643  size_t baseLen;
21644 
21645  ma_assert(id != NULL);
21646  ma_assert(idSize > 0);
21647  ma_assert(deviceIndex >= 0);
21648 
21649  baseLen = strlen(base);
21650  ma_assert(idSize > baseLen);
21651 
21652  ma_strcpy_s(id, idSize, base);
21653  ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10);
21654 }
21655 
21656 ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut)
21657 {
21658  size_t idLen;
21659  size_t baseLen;
21660  const char* deviceIndexStr;
21661 
21662  ma_assert(id != NULL);
21663  ma_assert(base != NULL);
21664  ma_assert(pIndexOut != NULL);
21665 
21666  idLen = strlen(id);
21667  baseLen = strlen(base);
21668  if (idLen <= baseLen) {
21669  return MA_ERROR; /* Doesn't look like the id starts with the base. */
21670  }
21671 
21672  if (strncmp(id, base, baseLen) != 0) {
21673  return MA_ERROR; /* ID does not begin with base. */
21674  }
21675 
21676  deviceIndexStr = id + baseLen;
21677  if (deviceIndexStr[0] == '\0') {
21678  return MA_ERROR; /* No index specified in the ID. */
21679  }
21680 
21681  if (pIndexOut) {
21682  *pIndexOut = atoi(deviceIndexStr);
21683  }
21684 
21685  return MA_SUCCESS;
21686 }
21687 
21688 ma_bool32 ma_context_is_device_id_equal__audio4(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
21689 {
21690  ma_assert(pContext != NULL);
21691  ma_assert(pID0 != NULL);
21692  ma_assert(pID1 != NULL);
21693  (void)pContext;
21694 
21695  return ma_strcmp(pID0->audio4, pID1->audio4) == 0;
21696 }
21697 
21698 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
21699 ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision)
21700 {
21701  if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) {
21702  return ma_format_u8;
21703  } else {
21704  if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) {
21705  if (precision == 16) {
21706  return ma_format_s16;
21707  } else if (precision == 24) {
21708  return ma_format_s24;
21709  } else if (precision == 32) {
21710  return ma_format_s32;
21711  }
21712  } else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) {
21713  if (precision == 16) {
21714  return ma_format_s16;
21715  } else if (precision == 24) {
21716  return ma_format_s24;
21717  } else if (precision == 32) {
21718  return ma_format_s32;
21719  }
21720  }
21721  }
21722 
21723  return ma_format_unknown; /* Encoding not supported. */
21724 }
21725 
21726 void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision)
21727 {
21728  ma_assert(format != ma_format_unknown);
21729  ma_assert(pEncoding != NULL);
21730  ma_assert(pPrecision != NULL);
21731 
21732  switch (format)
21733  {
21734  case ma_format_u8:
21735  {
21736  *pEncoding = AUDIO_ENCODING_ULINEAR;
21737  *pPrecision = 8;
21738  } break;
21739 
21740  case ma_format_s24:
21741  {
21742  *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
21743  *pPrecision = 24;
21744  } break;
21745 
21746  case ma_format_s32:
21747  {
21748  *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
21749  *pPrecision = 32;
21750  } break;
21751 
21752  case ma_format_s16:
21753  case ma_format_f32:
21754  default:
21755  {
21756  *pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
21757  *pPrecision = 16;
21758  } break;
21759  }
21760 }
21761 
21762 ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo)
21763 {
21764  return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision);
21765 }
21766 #else
21767 ma_format ma_format_from_swpar__audio4(struct audio_swpar* par)
21768 {
21769  if (par->bits == 8 && par->bps == 1 && par->sig == 0) {
21770  return ma_format_u8;
21771  }
21772  if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) {
21773  return ma_format_s16;
21774  }
21775  if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) {
21776  return ma_format_s24;
21777  }
21778  if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) {
21779  return ma_format_f32;
21780  }
21781 
21782  /* Format not supported. */
21783  return ma_format_unknown;
21784 }
21785 #endif
21786 
21787 ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pInfoOut)
21788 {
21789  audio_device_t fdDevice;
21790 #if !defined(MA_AUDIO4_USE_NEW_API)
21791  int counter = 0;
21792  audio_info_t fdInfo;
21793 #else
21794  struct audio_swpar fdPar;
21795  ma_format format;
21796 #endif
21797 
21798  ma_assert(pContext != NULL);
21799  ma_assert(fd >= 0);
21800  ma_assert(pInfoOut != NULL);
21801 
21802  (void)pContext;
21803  (void)deviceType;
21804 
21805  if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) {
21806  return MA_ERROR; /* Failed to retrieve device info. */
21807  }
21808 
21809  /* Name. */
21810  ma_strcpy_s(pInfoOut->name, sizeof(pInfoOut->name), fdDevice.name);
21811 
21812 #if !defined(MA_AUDIO4_USE_NEW_API)
21813  /* Supported formats. We get this by looking at the encodings. */
21814  for (;;) {
21815  audio_encoding_t encoding;
21816  ma_format format;
21817 
21818  ma_zero_object(&encoding);
21819  encoding.index = counter;
21820  if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
21821  break;
21822  }
21823 
21824  format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision);
21825  if (format != ma_format_unknown) {
21826  pInfoOut->formats[pInfoOut->formatCount++] = format;
21827  }
21828 
21829  counter += 1;
21830  }
21831 
21832  if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
21833  return MA_ERROR;
21834  }
21835 
21836  if (deviceType == ma_device_type_playback) {
21837  pInfoOut->minChannels = fdInfo.play.channels;
21838  pInfoOut->maxChannels = fdInfo.play.channels;
21839  pInfoOut->minSampleRate = fdInfo.play.sample_rate;
21840  pInfoOut->maxSampleRate = fdInfo.play.sample_rate;
21841  } else {
21842  pInfoOut->minChannels = fdInfo.record.channels;
21843  pInfoOut->maxChannels = fdInfo.record.channels;
21844  pInfoOut->minSampleRate = fdInfo.record.sample_rate;
21845  pInfoOut->maxSampleRate = fdInfo.record.sample_rate;
21846  }
21847 #else
21848  if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
21849  return MA_ERROR;
21850  }
21851 
21852  format = ma_format_from_swpar__audio4(&fdPar);
21853  if (format == ma_format_unknown) {
21854  return MA_FORMAT_NOT_SUPPORTED;
21855  }
21856  pInfoOut->formats[pInfoOut->formatCount++] = format;
21857 
21858  if (deviceType == ma_device_type_playback) {
21859  pInfoOut->minChannels = fdPar.pchan;
21860  pInfoOut->maxChannels = fdPar.pchan;
21861  } else {
21862  pInfoOut->minChannels = fdPar.rchan;
21863  pInfoOut->maxChannels = fdPar.rchan;
21864  }
21865 
21866  pInfoOut->minSampleRate = fdPar.rate;
21867  pInfoOut->maxSampleRate = fdPar.rate;
21868 #endif
21869 
21870  return MA_SUCCESS;
21871 }
21872 
21873 ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
21874 {
21875  const int maxDevices = 64;
21876  char devpath[256];
21877  int iDevice;
21878 
21879  ma_assert(pContext != NULL);
21880  ma_assert(callback != NULL);
21881 
21882  /*
21883  Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN"
21884  version here since we can open it even when another process has control of the "/dev/audioN" device.
21885  */
21886  for (iDevice = 0; iDevice < maxDevices; ++iDevice) {
21887  struct stat st;
21888  int fd;
21889  ma_bool32 isTerminating = MA_FALSE;
21890 
21891  ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl");
21892  ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10);
21893 
21894  if (stat(devpath, &st) < 0) {
21895  break;
21896  }
21897 
21898  /* The device exists, but we need to check if it's usable as playback and/or capture. */
21899 
21900  /* Playback. */
21901  if (!isTerminating) {
21902  fd = open(devpath, O_RDONLY, 0);
21903  if (fd >= 0) {
21904  /* Supports playback. */
21905  ma_device_info deviceInfo;
21906  ma_zero_object(&deviceInfo);
21907  ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
21908  if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) {
21909  isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
21910  }
21911 
21912  close(fd);
21913  }
21914  }
21915 
21916  /* Capture. */
21917  if (!isTerminating) {
21918  fd = open(devpath, O_WRONLY, 0);
21919  if (fd >= 0) {
21920  /* Supports capture. */
21921  ma_device_info deviceInfo;
21922  ma_zero_object(&deviceInfo);
21923  ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
21924  if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) {
21925  isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
21926  }
21927 
21928  close(fd);
21929  }
21930  }
21931 
21932  if (isTerminating) {
21933  break;
21934  }
21935  }
21936 
21937  return MA_SUCCESS;
21938 }
21939 
21940 ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
21941 {
21942  int fd = -1;
21943  int deviceIndex = -1;
21944  char ctlid[256];
21945  ma_result result;
21946 
21947  ma_assert(pContext != NULL);
21948  (void)shareMode;
21949 
21950  /*
21951  We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number
21952  from the device ID which will be in "/dev/audioN" format.
21953  */
21954  if (pDeviceID == NULL) {
21955  /* Default device. */
21956  ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl");
21957  } else {
21958  /* Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN". */
21959  result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex);
21960  if (result != MA_SUCCESS) {
21961  return result;
21962  }
21963 
21964  ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex);
21965  }
21966 
21967  fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0);
21968  if (fd == -1) {
21969  return MA_NO_DEVICE;
21970  }
21971 
21972  if (deviceIndex == -1) {
21973  ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio");
21974  } else {
21975  ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex);
21976  }
21977 
21978  result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo);
21979 
21980  close(fd);
21981  return result;
21982 }
21983 
21984 void ma_device_uninit__audio4(ma_device* pDevice)
21985 {
21986  ma_assert(pDevice != NULL);
21987 
21988  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
21989  close(pDevice->audio4.fdCapture);
21990  }
21991 
21992  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
21993  close(pDevice->audio4.fdPlayback);
21994  }
21995 }
21996 
21997 ma_result ma_device_init_fd__audio4(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
21998 {
21999  const char* pDefaultDeviceNames[] = {
22000  "/dev/audio",
22001  "/dev/audio0"
22002  };
22003  int fd;
22004  int fdFlags = 0;
22005 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
22006  audio_info_t fdInfo;
22007 #else
22008  struct audio_swpar fdPar;
22009 #endif
22010  ma_format internalFormat;
22011  ma_uint32 internalChannels;
22012  ma_uint32 internalSampleRate;
22013  ma_uint32 internalBufferSizeInFrames;
22014  ma_uint32 internalPeriods;
22015 
22016  ma_assert(pContext != NULL);
22017  ma_assert(pConfig != NULL);
22018  ma_assert(deviceType != ma_device_type_duplex);
22019  ma_assert(pDevice != NULL);
22020 
22021  (void)pContext;
22022 
22023  /* The first thing to do is open the file. */
22024  if (deviceType == ma_device_type_capture) {
22025  fdFlags = O_RDONLY;
22026  } else {
22027  fdFlags = O_WRONLY;
22028  }
22029  /*fdFlags |= O_NONBLOCK;*/
22030 
22031  if ((deviceType == ma_device_type_capture && pConfig->capture.pDeviceID == NULL) || (deviceType == ma_device_type_playback && pConfig->playback.pDeviceID == NULL)) {
22032  /* Default device. */
22033  size_t iDevice;
22034  for (iDevice = 0; iDevice < ma_countof(pDefaultDeviceNames); ++iDevice) {
22035  fd = open(pDefaultDeviceNames[iDevice], fdFlags, 0);
22036  if (fd != -1) {
22037  break;
22038  }
22039  }
22040  } else {
22041  /* Specific device. */
22042  fd = open((deviceType == ma_device_type_capture) ? pConfig->capture.pDeviceID->audio4 : pConfig->playback.pDeviceID->audio4, fdFlags, 0);
22043  }
22044 
22045  if (fd == -1) {
22046  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22047  }
22048 
22049 #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */
22050  AUDIO_INITINFO(&fdInfo);
22051 
22052  /* We get the driver to do as much of the data conversion as possible. */
22053  if (deviceType == ma_device_type_capture) {
22054  fdInfo.mode = AUMODE_RECORD;
22055  ma_encoding_from_format__audio4(pConfig->capture.format, &fdInfo.record.encoding, &fdInfo.record.precision);
22056  fdInfo.record.channels = pConfig->capture.channels;
22057  fdInfo.record.sample_rate = pConfig->sampleRate;
22058  } else {
22059  fdInfo.mode = AUMODE_PLAY;
22060  ma_encoding_from_format__audio4(pConfig->playback.format, &fdInfo.play.encoding, &fdInfo.play.precision);
22061  fdInfo.play.channels = pConfig->playback.channels;
22062  fdInfo.play.sample_rate = pConfig->sampleRate;
22063  }
22064 
22065  if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
22066  close(fd);
22067  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
22068  }
22069 
22070  if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
22071  close(fd);
22072  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
22073  }
22074 
22075  if (deviceType == ma_device_type_capture) {
22076  internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record);
22077  internalChannels = fdInfo.record.channels;
22078  internalSampleRate = fdInfo.record.sample_rate;
22079  } else {
22080  internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play);
22081  internalChannels = fdInfo.play.channels;
22082  internalSampleRate = fdInfo.play.sample_rate;
22083  }
22084 
22085  if (internalFormat == ma_format_unknown) {
22086  close(fd);
22087  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable.", MA_FORMAT_NOT_SUPPORTED);
22088  }
22089 
22090  /* Buffer. */
22091  {
22092  ma_uint32 internalBufferSizeInBytes;
22093 
22094  internalBufferSizeInFrames = pConfig->bufferSizeInFrames;
22095  if (internalBufferSizeInFrames == 0) {
22096  internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, internalSampleRate);
22097  }
22098 
22099  internalBufferSizeInBytes = internalBufferSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
22100  if (internalBufferSizeInBytes < 16) {
22101  internalBufferSizeInBytes = 16;
22102  }
22103 
22104  internalPeriods = pConfig->periods;
22105  if (internalPeriods < 2) {
22106  internalPeriods = 2;
22107  }
22108 
22109  /* What miniaudio calls a fragment, audio4 calls a block. */
22110  AUDIO_INITINFO(&fdInfo);
22111  fdInfo.hiwat = internalPeriods;
22112  fdInfo.lowat = internalPeriods-1;
22113  fdInfo.blocksize = internalBufferSizeInBytes / internalPeriods;
22114  if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
22115  close(fd);
22116  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed.", MA_FORMAT_NOT_SUPPORTED);
22117  }
22118 
22119  internalPeriods = fdInfo.hiwat;
22120  internalBufferSizeInFrames = (fdInfo.blocksize * fdInfo.hiwat) / ma_get_bytes_per_frame(internalFormat, internalChannels);
22121  }
22122 #else
22123  /* We need to retrieve the format of the device so we can know the channel count and sample rate. Then we can calculate the buffer size. */
22124  if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
22125  close(fd);
22126  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters.", MA_FORMAT_NOT_SUPPORTED);
22127  }
22128 
22129  internalFormat = ma_format_from_swpar__audio4(&fdPar);
22130  internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
22131  internalSampleRate = fdPar.rate;
22132 
22133  if (internalFormat == ma_format_unknown) {
22134  close(fd);
22135  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable.", MA_FORMAT_NOT_SUPPORTED);
22136  }
22137 
22138  /* Buffer. */
22139  {
22140  ma_uint32 internalBufferSizeInBytes;
22141 
22142  internalBufferSizeInFrames = pConfig->bufferSizeInFrames;
22143  if (internalBufferSizeInFrames == 0) {
22144  internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, internalSampleRate);
22145  }
22146 
22147  /* What miniaudio calls a fragment, audio4 calls a block. */
22148  internalBufferSizeInBytes = internalBufferSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
22149  if (internalBufferSizeInBytes < 16) {
22150  internalBufferSizeInBytes = 16;
22151  }
22152 
22153  fdPar.nblks = pConfig->periods;
22154  fdPar.round = internalBufferSizeInBytes / fdPar.nblks;
22155 
22156  if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) {
22157  close(fd);
22158  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters.", MA_FORMAT_NOT_SUPPORTED);
22159  }
22160 
22161  if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
22162  close(fd);
22163  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters.", MA_FORMAT_NOT_SUPPORTED);
22164  }
22165  }
22166 
22167  internalFormat = ma_format_from_swpar__audio4(&fdPar);
22168  internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
22169  internalSampleRate = fdPar.rate;
22170  internalPeriods = fdPar.nblks;
22171  internalBufferSizeInFrames = (fdPar.nblks * fdPar.round) / ma_get_bytes_per_frame(internalFormat, internalChannels);
22172 #endif
22173 
22174  if (internalFormat == ma_format_unknown) {
22175  close(fd);
22176  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable.", MA_FORMAT_NOT_SUPPORTED);
22177  }
22178 
22179  if (deviceType == ma_device_type_capture) {
22180  pDevice->audio4.fdCapture = fd;
22181  pDevice->capture.internalFormat = internalFormat;
22182  pDevice->capture.internalChannels = internalChannels;
22183  pDevice->capture.internalSampleRate = internalSampleRate;
22184  ma_get_standard_channel_map(ma_standard_channel_map_sound4, internalChannels, pDevice->capture.internalChannelMap);
22185  pDevice->capture.internalBufferSizeInFrames = internalBufferSizeInFrames;
22186  pDevice->capture.internalPeriods = internalPeriods;
22187  } else {
22188  pDevice->audio4.fdPlayback = fd;
22189  pDevice->playback.internalFormat = internalFormat;
22190  pDevice->playback.internalChannels = internalChannels;
22191  pDevice->playback.internalSampleRate = internalSampleRate;
22192  ma_get_standard_channel_map(ma_standard_channel_map_sound4, internalChannels, pDevice->playback.internalChannelMap);
22193  pDevice->playback.internalBufferSizeInFrames = internalBufferSizeInFrames;
22194  pDevice->playback.internalPeriods = internalPeriods;
22195  }
22196 
22197  return MA_SUCCESS;
22198 }
22199 
22200 ma_result ma_device_init__audio4(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
22201 {
22202  ma_assert(pDevice != NULL);
22203 
22204  ma_zero_object(&pDevice->audio4);
22205 
22206  if (pConfig->deviceType == ma_device_type_loopback) {
22208  }
22209 
22210  pDevice->audio4.fdCapture = -1;
22211  pDevice->audio4.fdPlayback = -1;
22212 
22213  /*
22214  The version of the operating system dictates whether or not the device is exclusive or shared. NetBSD
22215  introduced in-kernel mixing which means it's shared. All other BSD flavours are exclusive as far as
22216  I'm aware.
22217  */
22218 #if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000
22219  /* NetBSD 8.0+ */
22223  }
22224 #else
22225  /* All other flavors. */
22226 #endif
22227 
22228  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
22229  ma_result result = ma_device_init_fd__audio4(pContext, pConfig, ma_device_type_capture, pDevice);
22230  if (result != MA_SUCCESS) {
22231  return result;
22232  }
22233  }
22234 
22235  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
22236  ma_result result = ma_device_init_fd__audio4(pContext, pConfig, ma_device_type_playback, pDevice);
22237  if (result != MA_SUCCESS) {
22238  if (pConfig->deviceType == ma_device_type_duplex) {
22239  close(pDevice->audio4.fdCapture);
22240  }
22241  return result;
22242  }
22243  }
22244 
22245  return MA_SUCCESS;
22246 }
22247 
22248 #if 0
22249 ma_result ma_device_start__audio4(ma_device* pDevice)
22250 {
22251  ma_assert(pDevice != NULL);
22252 
22253  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
22254  if (pDevice->audio4.fdCapture == -1) {
22255  return MA_INVALID_ARGS;
22256  }
22257  }
22258 
22259  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22260  if (pDevice->audio4.fdPlayback == -1) {
22261  return MA_INVALID_ARGS;
22262  }
22263  }
22264 
22265  return MA_SUCCESS;
22266 }
22267 #endif
22268 
22269 ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd)
22270 {
22271  if (fd == -1) {
22272  return MA_INVALID_ARGS;
22273  }
22274 
22275 #if !defined(MA_AUDIO4_USE_NEW_API)
22276  if (ioctl(fd, AUDIO_FLUSH, 0) < 0) {
22277  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
22278  }
22279 #else
22280  if (ioctl(fd, AUDIO_STOP, 0) < 0) {
22281  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
22282  }
22283 #endif
22284 
22285  return MA_SUCCESS;
22286 }
22287 
22288 ma_result ma_device_stop__audio4(ma_device* pDevice)
22289 {
22290  ma_assert(pDevice != NULL);
22291 
22292  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
22293  ma_result result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture);
22294  if (result != MA_SUCCESS) {
22295  return result;
22296  }
22297  }
22298 
22299  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22300  ma_result result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback);
22301  if (result != MA_SUCCESS) {
22302  return result;
22303  }
22304  }
22305 
22306  return MA_SUCCESS;
22307 }
22308 
22309 ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
22310 {
22311  int result;
22312 
22313  if (pFramesWritten != NULL) {
22314  *pFramesWritten = 0;
22315  }
22316 
22317  result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
22318  if (result < 0) {
22319  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
22320  }
22321 
22322  if (pFramesWritten != NULL) {
22323  *pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
22324  }
22325 
22326  return MA_SUCCESS;
22327 }
22328 
22329 ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
22330 {
22331  int result;
22332 
22333  if (pFramesRead != NULL) {
22334  *pFramesRead = 0;
22335  }
22336 
22337  result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
22338  if (result < 0) {
22339  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device.", MA_FAILED_TO_READ_DATA_FROM_DEVICE);
22340  }
22341 
22342  if (pFramesRead != NULL) {
22343  *pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
22344  }
22345 
22346  return MA_SUCCESS;
22347 }
22348 
22349 ma_result ma_device_main_loop__audio4(ma_device* pDevice)
22350 {
22352  ma_bool32 exitLoop = MA_FALSE;
22353 
22354  /* No need to explicitly start the device like the other backends. */
22355 
22356  while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
22357  switch (pDevice->type)
22358  {
22359  case ma_device_type_duplex:
22360  {
22361  /* The process is: device_read -> convert -> callback -> convert -> device_write */
22362  ma_uint8 capturedDeviceData[8192];
22363  ma_uint8 playbackDeviceData[8192];
22364  ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
22365  ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
22366 
22367  ma_uint32 totalFramesProcessed = 0;
22368  ma_uint32 periodSizeInFrames = ma_min(pDevice->capture.internalBufferSizeInFrames/pDevice->capture.internalPeriods, pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods);
22369 
22370  while (totalFramesProcessed < periodSizeInFrames) {
22371  ma_uint32 framesRemaining = periodSizeInFrames - totalFramesProcessed;
22372  ma_uint32 framesProcessed;
22373  ma_uint32 framesToProcess = framesRemaining;
22374  if (framesToProcess > capturedDeviceDataCapInFrames) {
22375  framesToProcess = capturedDeviceDataCapInFrames;
22376  }
22377 
22378  result = ma_device_read__audio4(pDevice, capturedDeviceData, framesToProcess, &framesProcessed);
22379  if (result != MA_SUCCESS) {
22380  exitLoop = MA_TRUE;
22381  break;
22382  }
22383 
22384  pDevice->capture._dspFrameCount = framesToProcess;
22385  pDevice->capture._dspFrames = capturedDeviceData;
22386 
22387  for (;;) {
22388  ma_uint8 capturedData[8192];
22389  ma_uint8 playbackData[8192];
22390  ma_uint32 capturedDataCapInFrames = sizeof(capturedData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
22391  ma_uint32 playbackDataCapInFrames = sizeof(playbackData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
22392 
22393  ma_uint32 capturedFramesToTryProcessing = ma_min(capturedDataCapInFrames, playbackDataCapInFrames);
22394  ma_uint32 capturedFramesToProcess = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, capturedData, capturedFramesToTryProcessing);
22395  if (capturedFramesToProcess == 0) {
22396  break; /* Don't fire the data callback with zero frames. */
22397  }
22398 
22399  ma_device__on_data(pDevice, playbackData, capturedData, capturedFramesToProcess);
22400 
22401  /* At this point the playbackData buffer should be holding data that needs to be written to the device. */
22402  pDevice->playback._dspFrameCount = capturedFramesToProcess;
22403  pDevice->playback._dspFrames = playbackData;
22404  for (;;) {
22405  ma_uint32 playbackDeviceFramesCount = (ma_uint32)ma_pcm_converter_read(&pDevice->playback.converter, playbackDeviceData, playbackDeviceDataCapInFrames);
22406  if (playbackDeviceFramesCount == 0) {
22407  break;
22408  }
22409 
22410  result = ma_device_write__audio4(pDevice, playbackDeviceData, playbackDeviceFramesCount, NULL);
22411  if (result != MA_SUCCESS) {
22412  exitLoop = MA_TRUE;
22413  break;
22414  }
22415 
22416  if (playbackDeviceFramesCount < playbackDeviceDataCapInFrames) {
22417  break;
22418  }
22419  }
22420 
22421  if (capturedFramesToProcess < capturedFramesToTryProcessing) {
22422  break;
22423  }
22424 
22425  /* In case an error happened from ma_device_write2__alsa()... */
22426  if (result != MA_SUCCESS) {
22427  exitLoop = MA_TRUE;
22428  break;
22429  }
22430  }
22431 
22432  totalFramesProcessed += framesProcessed;
22433  }
22434  } break;
22435 
22437  {
22438  /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
22439  ma_uint8 intermediaryBuffer[8192];
22440  ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
22441  ma_uint32 periodSizeInFrames = pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods;
22442  ma_uint32 framesReadThisPeriod = 0;
22443  while (framesReadThisPeriod < periodSizeInFrames) {
22444  ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
22445  ma_uint32 framesProcessed;
22446  ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
22447  if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
22448  framesToReadThisIteration = intermediaryBufferSizeInFrames;
22449  }
22450 
22451  result = ma_device_read__audio4(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
22452  if (result != MA_SUCCESS) {
22453  exitLoop = MA_TRUE;
22454  break;
22455  }
22456 
22457  ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
22458 
22459  framesReadThisPeriod += framesProcessed;
22460  }
22461  } break;
22462 
22464  {
22465  /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
22466  ma_uint8 intermediaryBuffer[8192];
22467  ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
22468  ma_uint32 periodSizeInFrames = pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods;
22469  ma_uint32 framesWrittenThisPeriod = 0;
22470  while (framesWrittenThisPeriod < periodSizeInFrames) {
22471  ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
22472  ma_uint32 framesProcessed;
22473  ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
22474  if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
22475  framesToWriteThisIteration = intermediaryBufferSizeInFrames;
22476  }
22477 
22478  ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
22479 
22480  result = ma_device_write__audio4(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
22481  if (result != MA_SUCCESS) {
22482  exitLoop = MA_TRUE;
22483  break;
22484  }
22485 
22486  framesWrittenThisPeriod += framesProcessed;
22487  }
22488  } break;
22489 
22490  /* To silence a warning. Will never hit this. */
22492  default: break;
22493  }
22494  }
22495 
22496 
22497  /* Here is where the device is stopped. */
22498  ma_device_stop__audio4(pDevice);
22499 
22500  return result;
22501 }
22502 
22503 ma_result ma_context_uninit__audio4(ma_context* pContext)
22504 {
22505  ma_assert(pContext != NULL);
22506  ma_assert(pContext->backend == ma_backend_audio4);
22507 
22508  (void)pContext;
22509  return MA_SUCCESS;
22510 }
22511 
22512 ma_result ma_context_init__audio4(const ma_context_config* pConfig, ma_context* pContext)
22513 {
22514  ma_assert(pContext != NULL);
22515 
22516  (void)pConfig;
22517 
22518  pContext->onUninit = ma_context_uninit__audio4;
22519  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__audio4;
22520  pContext->onEnumDevices = ma_context_enumerate_devices__audio4;
22521  pContext->onGetDeviceInfo = ma_context_get_device_info__audio4;
22522  pContext->onDeviceInit = ma_device_init__audio4;
22523  pContext->onDeviceUninit = ma_device_uninit__audio4;
22524  pContext->onDeviceStart = NULL; /* Not required for synchronous backends. */
22525  pContext->onDeviceStop = NULL; /* Not required for synchronous backends. */
22526  pContext->onDeviceMainLoop = ma_device_main_loop__audio4;
22527 
22528  return MA_SUCCESS;
22529 }
22530 #endif /* audio4 */
22531 
22532 
22533 /******************************************************************************
22534 
22535 OSS Backend
22536 
22537 ******************************************************************************/
22538 #ifdef MA_HAS_OSS
22539 #include <sys/ioctl.h>
22540 #include <unistd.h>
22541 #include <fcntl.h>
22542 #include <sys/soundcard.h>
22543 
22544 #ifndef SNDCTL_DSP_HALT
22545 #define SNDCTL_DSP_HALT SNDCTL_DSP_RESET
22546 #endif
22547 
22548 int ma_open_temp_device__oss()
22549 {
22550  /* The OSS sample code uses "/dev/mixer" as the device for getting system properties so I'm going to do the same. */
22551  int fd = open("/dev/mixer", O_RDONLY, 0);
22552  if (fd >= 0) {
22553  return fd;
22554  }
22555 
22556  return -1;
22557 }
22558 
22559 ma_result ma_context_open_device__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, int* pfd)
22560 {
22561  const char* deviceName;
22562  int flags;
22563 
22564  ma_assert(pContext != NULL);
22565  ma_assert(pfd != NULL);
22566  (void)pContext;
22567 
22568  *pfd = -1;
22569 
22570  /* This function should only be called for playback or capture, not duplex. */
22571  if (deviceType == ma_device_type_duplex) {
22572  return MA_INVALID_ARGS;
22573  }
22574 
22575  deviceName = "/dev/dsp";
22576  if (pDeviceID != NULL) {
22577  deviceName = pDeviceID->oss;
22578  }
22579 
22580  flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY;
22581  if (shareMode == ma_share_mode_exclusive) {
22582  flags |= O_EXCL;
22583  }
22584 
22585  *pfd = open(deviceName, flags, 0);
22586  if (*pfd == -1) {
22588  }
22589 
22590  return MA_SUCCESS;
22591 }
22592 
22593 ma_bool32 ma_context_is_device_id_equal__oss(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
22594 {
22595  ma_assert(pContext != NULL);
22596  ma_assert(pID0 != NULL);
22597  ma_assert(pID1 != NULL);
22598  (void)pContext;
22599 
22600  return ma_strcmp(pID0->oss, pID1->oss) == 0;
22601 }
22602 
22603 ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
22604 {
22605  int fd;
22606  oss_sysinfo si;
22607  int result;
22608 
22609  ma_assert(pContext != NULL);
22610  ma_assert(callback != NULL);
22611 
22612  fd = ma_open_temp_device__oss();
22613  if (fd == -1) {
22614  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.", MA_NO_BACKEND);
22615  }
22616 
22617  result = ioctl(fd, SNDCTL_SYSINFO, &si);
22618  if (result != -1) {
22619  int iAudioDevice;
22620  for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
22621  oss_audioinfo ai;
22622  ai.dev = iAudioDevice;
22623  result = ioctl(fd, SNDCTL_AUDIOINFO, &ai);
22624  if (result != -1) {
22625  if (ai.devnode[0] != '\0') { /* <-- Can be blank, according to documentation. */
22626  ma_device_info deviceInfo;
22627  ma_bool32 isTerminating = MA_FALSE;
22628 
22629  ma_zero_object(&deviceInfo);
22630 
22631  /* ID */
22632  ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1);
22633 
22634  /*
22635  The human readable device name should be in the "ai.handle" variable, but it can
22636  sometimes be empty in which case we just fall back to "ai.name" which is less user
22637  friendly, but usually has a value.
22638  */
22639  if (ai.handle[0] != '\0') {
22640  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1);
22641  } else {
22642  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1);
22643  }
22644 
22645  /* The device can be both playback and capture. */
22646  if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) {
22647  isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
22648  }
22649  if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) {
22650  isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
22651  }
22652 
22653  if (isTerminating) {
22654  break;
22655  }
22656  }
22657  }
22658  }
22659  } else {
22660  close(fd);
22661  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND);
22662  }
22663 
22664  close(fd);
22665  return MA_SUCCESS;
22666 }
22667 
22668 ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
22669 {
22670  ma_bool32 foundDevice;
22671  int fdTemp;
22672  oss_sysinfo si;
22673  int result;
22674 
22675  ma_assert(pContext != NULL);
22676  (void)shareMode;
22677 
22678  /* Handle the default device a little differently. */
22679  if (pDeviceID == NULL) {
22680  if (deviceType == ma_device_type_playback) {
22681  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
22682  } else {
22683  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
22684  }
22685 
22686  return MA_SUCCESS;
22687  }
22688 
22689 
22690  /* If we get here it means we are _not_ using the default device. */
22691  foundDevice = MA_FALSE;
22692 
22693  fdTemp = ma_open_temp_device__oss();
22694  if (fdTemp == -1) {
22695  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.", MA_NO_BACKEND);
22696  }
22697 
22698  result = ioctl(fdTemp, SNDCTL_SYSINFO, &si);
22699  if (result != -1) {
22700  int iAudioDevice;
22701  for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
22702  oss_audioinfo ai;
22703  ai.dev = iAudioDevice;
22704  result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai);
22705  if (result != -1) {
22706  if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) {
22707  /* It has the same name, so now just confirm the type. */
22708  if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) ||
22709  (deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) {
22710  unsigned int formatMask;
22711 
22712  /* ID */
22713  ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1);
22714 
22715  /*
22716  The human readable device name should be in the "ai.handle" variable, but it can
22717  sometimes be empty in which case we just fall back to "ai.name" which is less user
22718  friendly, but usually has a value.
22719  */
22720  if (ai.handle[0] != '\0') {
22721  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1);
22722  } else {
22723  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1);
22724  }
22725 
22726  pDeviceInfo->minChannels = ai.min_channels;
22727  pDeviceInfo->maxChannels = ai.max_channels;
22728  pDeviceInfo->minSampleRate = ai.min_rate;
22729  pDeviceInfo->maxSampleRate = ai.max_rate;
22730  pDeviceInfo->formatCount = 0;
22731 
22732  if (deviceType == ma_device_type_playback) {
22733  formatMask = ai.oformats;
22734  } else {
22735  formatMask = ai.iformats;
22736  }
22737 
22738  if ((formatMask & AFMT_U8) != 0) {
22739  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_u8;
22740  }
22741  if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) {
22742  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s16;
22743  }
22744  if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) {
22745  pDeviceInfo->formats[pDeviceInfo->formatCount++] = ma_format_s32;
22746  }
22747 
22748  foundDevice = MA_TRUE;
22749  break;
22750  }
22751  }
22752  }
22753  }
22754  } else {
22755  close(fdTemp);
22756  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.", MA_NO_BACKEND);
22757  }
22758 
22759 
22760  close(fdTemp);
22761 
22762  if (!foundDevice) {
22763  return MA_NO_DEVICE;
22764  }
22765 
22766  return MA_SUCCESS;
22767 }
22768 
22769 void ma_device_uninit__oss(ma_device* pDevice)
22770 {
22771  ma_assert(pDevice != NULL);
22772 
22773  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
22774  close(pDevice->oss.fdCapture);
22775  }
22776 
22777  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
22778  close(pDevice->oss.fdPlayback);
22779  }
22780 }
22781 
22782 int ma_format_to_oss(ma_format format)
22783 {
22784  int ossFormat = AFMT_U8;
22785  switch (format) {
22786  case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
22787  case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
22788  case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
22789  case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
22790  case ma_format_u8:
22791  default: ossFormat = AFMT_U8; break;
22792  }
22793 
22794  return ossFormat;
22795 }
22796 
22797 ma_format ma_format_from_oss(int ossFormat)
22798 {
22799  if (ossFormat == AFMT_U8) {
22800  return ma_format_u8;
22801  } else {
22802  if (ma_is_little_endian()) {
22803  switch (ossFormat) {
22804  case AFMT_S16_LE: return ma_format_s16;
22805  case AFMT_S32_LE: return ma_format_s32;
22806  default: return ma_format_unknown;
22807  }
22808  } else {
22809  switch (ossFormat) {
22810  case AFMT_S16_BE: return ma_format_s16;
22811  case AFMT_S32_BE: return ma_format_s32;
22812  default: return ma_format_unknown;
22813  }
22814  }
22815  }
22816 
22817  return ma_format_unknown;
22818 }
22819 
22820 ma_result ma_device_init_fd__oss(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
22821 {
22822  ma_result result;
22823  int ossResult;
22824  int fd;
22825  const ma_device_id* pDeviceID = NULL;
22826  ma_share_mode shareMode;
22827  int ossFormat;
22828  int ossChannels;
22829  int ossSampleRate;
22830  int ossFragment;
22831 
22832  ma_assert(pContext != NULL);
22833  ma_assert(pConfig != NULL);
22834  ma_assert(deviceType != ma_device_type_duplex);
22835  ma_assert(pDevice != NULL);
22836 
22837  (void)pContext;
22838 
22839  if (deviceType == ma_device_type_capture) {
22840  pDeviceID = pConfig->capture.pDeviceID;
22841  shareMode = pConfig->capture.shareMode;
22842  ossFormat = ma_format_to_oss(pConfig->capture.format);
22843  ossChannels = (int)pConfig->capture.channels;
22844  ossSampleRate = (int)pConfig->sampleRate;
22845  } else {
22846  pDeviceID = pConfig->playback.pDeviceID;
22847  shareMode = pConfig->playback.shareMode;
22848  ossFormat = ma_format_to_oss(pConfig->playback.format);
22849  ossChannels = (int)pConfig->playback.channels;
22850  ossSampleRate = (int)pConfig->sampleRate;
22851  }
22852 
22853  result = ma_context_open_device__oss(pContext, deviceType, pDeviceID, shareMode, &fd);
22854  if (result != MA_SUCCESS) {
22855  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22856  }
22857 
22858  /*
22859  The OSS documantation is very clear about the order we should be initializing the device's properties:
22860  1) Format
22861  2) Channels
22862  3) Sample rate.
22863  */
22864 
22865  /* Format. */
22866  ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat);
22867  if (ossResult == -1) {
22868  close(fd);
22869  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format.", MA_FORMAT_NOT_SUPPORTED);
22870  }
22871 
22872  /* Channels. */
22873  ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels);
22874  if (ossResult == -1) {
22875  close(fd);
22876  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count.", MA_FORMAT_NOT_SUPPORTED);
22877  }
22878 
22879  /* Sample Rate. */
22880  ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate);
22881  if (ossResult == -1) {
22882  close(fd);
22883  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate.", MA_FORMAT_NOT_SUPPORTED);
22884  }
22885 
22886  /*
22887  Buffer.
22888 
22889  The documentation says that the fragment settings should be set as soon as possible, but I'm not sure if
22890  it should be done before or after format/channels/rate.
22891 
22892  OSS wants the fragment size in bytes and a power of 2. When setting, we specify the power, not the actual
22893  value.
22894  */
22895  {
22896  ma_uint32 fragmentSizeInBytes;
22897  ma_uint32 bufferSizeInFrames;
22898  ma_uint32 ossFragmentSizePower;
22899 
22900  bufferSizeInFrames = pConfig->bufferSizeInFrames;
22901  if (bufferSizeInFrames == 0) {
22902  bufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, (ma_uint32)ossSampleRate);
22903  }
22904 
22905  fragmentSizeInBytes = ma_round_to_power_of_2((bufferSizeInFrames / pConfig->periods) * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels));
22906  if (fragmentSizeInBytes < 16) {
22907  fragmentSizeInBytes = 16;
22908  }
22909 
22910  ossFragmentSizePower = 4;
22911  fragmentSizeInBytes >>= 4;
22912  while (fragmentSizeInBytes >>= 1) {
22913  ossFragmentSizePower += 1;
22914  }
22915 
22916  ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower);
22917  ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment);
22918  if (ossResult == -1) {
22919  close(fd);
22920  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count.", MA_FORMAT_NOT_SUPPORTED);
22921  }
22922  }
22923 
22924  /* Internal settings. */
22925  if (deviceType == ma_device_type_capture) {
22926  pDevice->oss.fdCapture = fd;
22927  pDevice->capture.internalFormat = ma_format_from_oss(ossFormat);
22928  pDevice->capture.internalChannels = ossChannels;
22929  pDevice->capture.internalSampleRate = ossSampleRate;
22930  ma_get_standard_channel_map(ma_standard_channel_map_sound4, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
22931  pDevice->capture.internalPeriods = (ma_uint32)(ossFragment >> 16);
22932  pDevice->capture.internalBufferSizeInFrames = (((ma_uint32)(1 << (ossFragment & 0xFFFF))) * pDevice->capture.internalPeriods) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
22933 
22934  if (pDevice->capture.internalFormat == ma_format_unknown) {
22935  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
22936  }
22937  } else {
22938  pDevice->oss.fdPlayback = fd;
22939  pDevice->playback.internalFormat = ma_format_from_oss(ossFormat);
22940  pDevice->playback.internalChannels = ossChannels;
22941  pDevice->playback.internalSampleRate = ossSampleRate;
22942  ma_get_standard_channel_map(ma_standard_channel_map_sound4, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
22943  pDevice->playback.internalPeriods = (ma_uint32)(ossFragment >> 16);
22944  pDevice->playback.internalBufferSizeInFrames = (((ma_uint32)(1 << (ossFragment & 0xFFFF))) * pDevice->playback.internalPeriods) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
22945 
22946  if (pDevice->playback.internalFormat == ma_format_unknown) {
22947  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.", MA_FORMAT_NOT_SUPPORTED);
22948  }
22949  }
22950 
22951  return MA_SUCCESS;
22952 }
22953 
22954 ma_result ma_device_init__oss(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
22955 {
22956  ma_assert(pContext != NULL);
22957  ma_assert(pConfig != NULL);
22958  ma_assert(pDevice != NULL);
22959 
22960  ma_zero_object(&pDevice->oss);
22961 
22962  if (pConfig->deviceType == ma_device_type_loopback) {
22964  }
22965 
22966  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
22967  ma_result result = ma_device_init_fd__oss(pContext, pConfig, ma_device_type_capture, pDevice);
22968  if (result != MA_SUCCESS) {
22969  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22970  }
22971  }
22972 
22973  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
22974  ma_result result = ma_device_init_fd__oss(pContext, pConfig, ma_device_type_playback, pDevice);
22975  if (result != MA_SUCCESS) {
22976  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
22977  }
22978  }
22979 
22980  return MA_SUCCESS;
22981 }
22982 
22983 ma_result ma_device_stop__oss(ma_device* pDevice)
22984 {
22985  ma_assert(pDevice != NULL);
22986 
22987  /*
22988  We want to use SNDCTL_DSP_HALT. From the documentation:
22989 
22990  In multithreaded applications SNDCTL_DSP_HALT (SNDCTL_DSP_RESET) must only be called by the thread
22991  that actually reads/writes the audio device. It must not be called by some master thread to kill the
22992  audio thread. The audio thread will not stop or get any kind of notification that the device was
22993  stopped by the master thread. The device gets stopped but the next read or write call will silently
22994  restart the device.
22995 
22996  This is actually safe in our case, because this function is only ever called from within our worker
22997  thread anyway. Just keep this in mind, though...
22998  */
22999 
23000  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23001  int result = ioctl(pDevice->oss.fdCapture, SNDCTL_DSP_HALT, 0);
23002  if (result == -1) {
23003  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to stop device. SNDCTL_DSP_HALT failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
23004  }
23005  }
23006 
23007  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23008  int result = ioctl(pDevice->oss.fdPlayback, SNDCTL_DSP_HALT, 0);
23009  if (result == -1) {
23010  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to stop device. SNDCTL_DSP_HALT failed.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
23011  }
23012  }
23013 
23014  return MA_SUCCESS;
23015 }
23016 
23017 ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
23018 {
23019  int resultOSS;
23020 
23021  if (pFramesWritten != NULL) {
23022  *pFramesWritten = 0;
23023  }
23024 
23025  resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
23026  if (resultOSS < 0) {
23027  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device.", MA_FAILED_TO_SEND_DATA_TO_DEVICE);
23028  }
23029 
23030  if (pFramesWritten != NULL) {
23031  *pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
23032  }
23033 
23034  return MA_SUCCESS;
23035 }
23036 
23037 ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
23038 {
23039  int resultOSS;
23040 
23041  if (pFramesRead != NULL) {
23042  *pFramesRead = 0;
23043  }
23044 
23045  resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
23046  if (resultOSS < 0) {
23047  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client.", MA_FAILED_TO_READ_DATA_FROM_DEVICE);
23048  }
23049 
23050  if (pFramesRead != NULL) {
23051  *pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
23052  }
23053 
23054  return MA_SUCCESS;
23055 }
23056 
23057 ma_result ma_device_main_loop__oss(ma_device* pDevice)
23058 {
23060  ma_bool32 exitLoop = MA_FALSE;
23061 
23062  /* No need to explicitly start the device like the other backends. */
23063 
23064  while (ma_device__get_state(pDevice) == MA_STATE_STARTED && !exitLoop) {
23065  switch (pDevice->type)
23066  {
23067  case ma_device_type_duplex:
23068  {
23069  /* The process is: device_read -> convert -> callback -> convert -> device_write */
23070  ma_uint8 capturedDeviceData[8192];
23071  ma_uint8 playbackDeviceData[8192];
23072  ma_uint32 capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
23073  ma_uint32 playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
23074 
23075  ma_uint32 totalFramesProcessed = 0;
23076  ma_uint32 periodSizeInFrames = ma_min(pDevice->capture.internalBufferSizeInFrames/pDevice->capture.internalPeriods, pDevice->playback.internalBufferSizeInFrames/pDevice->playback.internalPeriods);
23077 
23078  while (totalFramesProcessed < periodSizeInFrames) {
23079  ma_uint32 framesRemaining = periodSizeInFrames - totalFramesProcessed;
23080  ma_uint32 framesProcessed;
23081  ma_uint32 framesToProcess = framesRemaining;
23082  if (framesToProcess > capturedDeviceDataCapInFrames) {
23083  framesToProcess = capturedDeviceDataCapInFrames;
23084  }
23085 
23086  result = ma_device_read__oss(pDevice, capturedDeviceData, framesToProcess, &framesProcessed);
23087  if (result != MA_SUCCESS) {
23088  exitLoop = MA_TRUE;
23089  break;
23090  }
23091 
23092  pDevice->capture._dspFrameCount = framesToProcess;
23093  pDevice->capture._dspFrames = capturedDeviceData;
23094 
23095  for (;;) {
23096  ma_uint8 capturedData[8192];
23097  ma_uint8 playbackData[8192];
23098  ma_uint32 capturedDataCapInFrames = sizeof(capturedData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
23099  ma_uint32 playbackDataCapInFrames = sizeof(playbackData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
23100 
23101  ma_uint32 capturedFramesToTryProcessing = ma_min(capturedDataCapInFrames, playbackDataCapInFrames);
23102  ma_uint32 capturedFramesToProcess = (ma_uint32)ma_pcm_converter_read(&pDevice->capture.converter, capturedData, capturedFramesToTryProcessing);
23103  if (capturedFramesToProcess == 0) {
23104  break; /* Don't fire the data callback with zero frames. */
23105  }
23106 
23107  ma_device__on_data(pDevice, playbackData, capturedData, capturedFramesToProcess);
23108 
23109  /* At this point the playbackData buffer should be holding data that needs to be written to the device. */
23110  pDevice->playback._dspFrameCount = capturedFramesToProcess;
23111  pDevice->playback._dspFrames = playbackData;
23112  for (;;) {
23113  ma_uint32 playbackDeviceFramesCount = (ma_uint32)ma_pcm_converter_read(&pDevice->playback.converter, playbackDeviceData, playbackDeviceDataCapInFrames);
23114  if (playbackDeviceFramesCount == 0) {
23115  break;
23116  }
23117 
23118  result = ma_device_write__oss(pDevice, playbackDeviceData, playbackDeviceFramesCount, NULL);
23119  if (result != MA_SUCCESS) {
23120  exitLoop = MA_TRUE;
23121  break;
23122  }
23123 
23124  if (playbackDeviceFramesCount < playbackDeviceDataCapInFrames) {
23125  break;
23126  }
23127  }
23128 
23129  if (capturedFramesToProcess < capturedFramesToTryProcessing) {
23130  break;
23131  }
23132 
23133  /* In case an error happened from ma_device_write2__alsa()... */
23134  if (result != MA_SUCCESS) {
23135  exitLoop = MA_TRUE;
23136  break;
23137  }
23138  }
23139 
23140  totalFramesProcessed += framesProcessed;
23141  }
23142  } break;
23143 
23145  {
23146  /* We read in chunks of the period size, but use a stack allocated buffer for the intermediary. */
23147  ma_uint8 intermediaryBuffer[8192];
23148  ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
23149  ma_uint32 periodSizeInFrames = pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods;
23150  ma_uint32 framesReadThisPeriod = 0;
23151  while (framesReadThisPeriod < periodSizeInFrames) {
23152  ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
23153  ma_uint32 framesProcessed;
23154  ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
23155  if (framesToReadThisIteration > intermediaryBufferSizeInFrames) {
23156  framesToReadThisIteration = intermediaryBufferSizeInFrames;
23157  }
23158 
23159  result = ma_device_read__oss(pDevice, intermediaryBuffer, framesToReadThisIteration, &framesProcessed);
23160  if (result != MA_SUCCESS) {
23161  exitLoop = MA_TRUE;
23162  break;
23163  }
23164 
23165  ma_device__send_frames_to_client(pDevice, framesProcessed, intermediaryBuffer);
23166 
23167  framesReadThisPeriod += framesProcessed;
23168  }
23169  } break;
23170 
23172  {
23173  /* We write in chunks of the period size, but use a stack allocated buffer for the intermediary. */
23174  ma_uint8 intermediaryBuffer[8192];
23175  ma_uint32 intermediaryBufferSizeInFrames = sizeof(intermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
23176  ma_uint32 periodSizeInFrames = pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods;
23177  ma_uint32 framesWrittenThisPeriod = 0;
23178  while (framesWrittenThisPeriod < periodSizeInFrames) {
23179  ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
23180  ma_uint32 framesProcessed;
23181  ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
23182  if (framesToWriteThisIteration > intermediaryBufferSizeInFrames) {
23183  framesToWriteThisIteration = intermediaryBufferSizeInFrames;
23184  }
23185 
23186  ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, intermediaryBuffer);
23187 
23188  result = ma_device_write__oss(pDevice, intermediaryBuffer, framesToWriteThisIteration, &framesProcessed);
23189  if (result != MA_SUCCESS) {
23190  exitLoop = MA_TRUE;
23191  break;
23192  }
23193 
23194  framesWrittenThisPeriod += framesProcessed;
23195  }
23196  } break;
23197 
23198  /* To silence a warning. Will never hit this. */
23200  default: break;
23201  }
23202  }
23203 
23204 
23205  /* Here is where the device is stopped. */
23206  ma_device_stop__oss(pDevice);
23207 
23208  return result;
23209 }
23210 
23211 ma_result ma_context_uninit__oss(ma_context* pContext)
23212 {
23213  ma_assert(pContext != NULL);
23214  ma_assert(pContext->backend == ma_backend_oss);
23215 
23216  (void)pContext;
23217  return MA_SUCCESS;
23218 }
23219 
23220 ma_result ma_context_init__oss(const ma_context_config* pConfig, ma_context* pContext)
23221 {
23222  int fd;
23223  int ossVersion;
23224  int result;
23225 
23226  ma_assert(pContext != NULL);
23227 
23228  (void)pConfig;
23229 
23230  /* Try opening a temporary device first so we can get version information. This is closed at the end. */
23231  fd = ma_open_temp_device__oss();
23232  if (fd == -1) {
23233  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties.", MA_NO_BACKEND); /* Looks liks OSS isn't installed, or there are no available devices. */
23234  }
23235 
23236  /* Grab the OSS version. */
23237  ossVersion = 0;
23238  result = ioctl(fd, OSS_GETVERSION, &ossVersion);
23239  if (result == -1) {
23240  close(fd);
23241  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version.", MA_NO_BACKEND);
23242  }
23243 
23244  pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16);
23245  pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8);
23246 
23247  pContext->onUninit = ma_context_uninit__oss;
23248  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__oss;
23249  pContext->onEnumDevices = ma_context_enumerate_devices__oss;
23250  pContext->onGetDeviceInfo = ma_context_get_device_info__oss;
23251  pContext->onDeviceInit = ma_device_init__oss;
23252  pContext->onDeviceUninit = ma_device_uninit__oss;
23253  pContext->onDeviceStart = NULL; /* Not required for synchronous backends. */
23254  pContext->onDeviceStop = NULL; /* Not required for synchronous backends. */
23255  pContext->onDeviceMainLoop = ma_device_main_loop__oss;
23256 
23257  close(fd);
23258  return MA_SUCCESS;
23259 }
23260 #endif /* OSS */
23261 
23262 
23263 /******************************************************************************
23264 
23265 AAudio Backend
23266 
23267 ******************************************************************************/
23268 #ifdef MA_HAS_AAUDIO
23269 /*#include <AAudio/AAudio.h>*/
23270 
23271 #define MA_AAUDIO_UNSPECIFIED 0
23272 
23273 typedef int32_t ma_aaudio_result_t;
23274 typedef int32_t ma_aaudio_direction_t;
23275 typedef int32_t ma_aaudio_sharing_mode_t;
23276 typedef int32_t ma_aaudio_format_t;
23277 typedef int32_t ma_aaudio_stream_state_t;
23278 typedef int32_t ma_aaudio_performance_mode_t;
23279 typedef int32_t ma_aaudio_data_callback_result_t;
23280 
23281 /* Result codes. miniaudio only cares about the success code. */
23282 #define MA_AAUDIO_OK 0
23283 
23284 /* Directions. */
23285 #define MA_AAUDIO_DIRECTION_OUTPUT 0
23286 #define MA_AAUDIO_DIRECTION_INPUT 1
23287 
23288 /* Sharing modes. */
23289 #define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0
23290 #define MA_AAUDIO_SHARING_MODE_SHARED 1
23291 
23292 /* Formats. */
23293 #define MA_AAUDIO_FORMAT_PCM_I16 1
23294 #define MA_AAUDIO_FORMAT_PCM_FLOAT 2
23295 
23296 /* Stream states. */
23297 #define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0
23298 #define MA_AAUDIO_STREAM_STATE_UNKNOWN 1
23299 #define MA_AAUDIO_STREAM_STATE_OPEN 2
23300 #define MA_AAUDIO_STREAM_STATE_STARTING 3
23301 #define MA_AAUDIO_STREAM_STATE_STARTED 4
23302 #define MA_AAUDIO_STREAM_STATE_PAUSING 5
23303 #define MA_AAUDIO_STREAM_STATE_PAUSED 6
23304 #define MA_AAUDIO_STREAM_STATE_FLUSHING 7
23305 #define MA_AAUDIO_STREAM_STATE_FLUSHED 8
23306 #define MA_AAUDIO_STREAM_STATE_STOPPING 9
23307 #define MA_AAUDIO_STREAM_STATE_STOPPED 10
23308 #define MA_AAUDIO_STREAM_STATE_CLOSING 11
23309 #define MA_AAUDIO_STREAM_STATE_CLOSED 12
23310 #define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13
23311 
23312 /* Performance modes. */
23313 #define MA_AAUDIO_PERFORMANCE_MODE_NONE 10
23314 #define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11
23315 #define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12
23316 
23317 /* Callback results. */
23318 #define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0
23319 #define MA_AAUDIO_CALLBACK_RESULT_STOP 1
23320 
23321 /* Objects. */
23322 typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder;
23323 typedef struct ma_AAudioStream_t* ma_AAudioStream;
23324 
23325 typedef ma_aaudio_data_callback_result_t (*ma_AAudioStream_dataCallback)(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames);
23326 
23327 typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder);
23328 typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder);
23329 typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId);
23330 typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction);
23331 typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode);
23332 typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format);
23333 typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount);
23334 typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate);
23335 typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
23336 typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
23337 typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData);
23338 typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode);
23339 typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream);
23340 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream);
23341 typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream);
23342 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange) (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds);
23343 typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream);
23344 typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream);
23345 typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream);
23346 typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream);
23347 typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream);
23348 typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream);
23349 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream);
23350 typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream);
23351 
23352 ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA)
23353 {
23354  switch (resultAA)
23355  {
23356  case MA_AAUDIO_OK: return MA_SUCCESS;
23357  default: break;
23358  }
23359 
23360  return MA_ERROR;
23361 }
23362 
23363 ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
23364 {
23365  ma_device* pDevice = (ma_device*)pUserData;
23366  ma_assert(pDevice != NULL);
23367 
23368  if (pDevice->type == ma_device_type_duplex) {
23369  ma_device__handle_duplex_callback_capture(pDevice, frameCount, pAudioData, &pDevice->aaudio.duplexRB);
23370  } else {
23371  ma_device__send_frames_to_client(pDevice, frameCount, pAudioData); /* Send directly to the client. */
23372  }
23373 
23374  (void)pStream;
23375  return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
23376 }
23377 
23378 ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
23379 {
23380  ma_device* pDevice = (ma_device*)pUserData;
23381  ma_assert(pDevice != NULL);
23382 
23383  if (pDevice->type == ma_device_type_duplex) {
23384  ma_device__handle_duplex_callback_playback(pDevice, frameCount, pAudioData, &pDevice->aaudio.duplexRB);
23385  } else {
23386  ma_device__read_frames_from_client(pDevice, frameCount, pAudioData); /* Read directly from the client. */
23387  }
23388 
23389  (void)pStream;
23390  return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
23391 }
23392 
23393 ma_result ma_open_stream__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, const ma_device_config* pConfig, const ma_device* pDevice, ma_AAudioStream** ppStream)
23394 {
23395  ma_AAudioStreamBuilder* pBuilder;
23396  ma_aaudio_result_t resultAA;
23397 
23398  ma_assert(deviceType != ma_device_type_duplex); /* This function should not be called for a full-duplex device type. */
23399 
23400  *ppStream = NULL;
23401 
23402  resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);
23403  if (resultAA != MA_AAUDIO_OK) {
23404  return ma_result_from_aaudio(resultAA);
23405  }
23406 
23407  if (pDeviceID != NULL) {
23408  ((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);
23409  }
23410 
23411  ((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT);
23412  ((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE);
23413 
23414  if (pConfig != NULL) {
23415  ma_uint32 bufferCapacityInFrames;
23416 
23417  if (pDevice == NULL || !pDevice->usingDefaultSampleRate) {
23418  ((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pConfig->sampleRate);
23419  }
23420 
23421  if (deviceType == ma_device_type_capture) {
23422  if (pDevice == NULL || !pDevice->capture.usingDefaultChannels) {
23423  ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pConfig->capture.channels);
23424  }
23425  if (pDevice == NULL || !pDevice->capture.usingDefaultFormat) {
23426  ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pConfig->capture.format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
23427  }
23428  } else {
23429  if (pDevice == NULL || !pDevice->playback.usingDefaultChannels) {
23430  ((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pConfig->playback.channels);
23431  }
23432  if (pDevice == NULL || !pDevice->playback.usingDefaultFormat) {
23433  ((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pConfig->playback.format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
23434  }
23435  }
23436 
23437  bufferCapacityInFrames = pConfig->bufferSizeInFrames;
23438  if (bufferCapacityInFrames == 0) {
23440  }
23441  bufferCapacityInFrames = (bufferCapacityInFrames / pConfig->periods) * pConfig->periods; /* <-- Make sure the buffer capacity is an even multiple of a period. */
23442  ((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames);
23443 
23444  ((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pConfig->periods);
23445 
23446  if (deviceType == ma_device_type_capture) {
23447  ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice);
23448  } else {
23449  ((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice);
23450  }
23451 
23452  /* Not sure how this affects things, but since there's a mapping between miniaudio's performance profiles and AAudio's performance modes, let go ahead and set it. */
23453  ((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE);
23454  }
23455 
23456  resultAA = ((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream);
23457  if (resultAA != MA_AAUDIO_OK) {
23458  *ppStream = NULL;
23459  ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
23460  return ma_result_from_aaudio(resultAA);
23461  }
23462 
23463  ((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
23464  return MA_SUCCESS;
23465 }
23466 
23467 ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream)
23468 {
23469  return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream));
23470 }
23471 
23472 ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType)
23473 {
23474  /* The only way to know this is to try creating a stream. */
23475  ma_AAudioStream* pStream;
23476  ma_result result = ma_open_stream__aaudio(pContext, deviceType, NULL, ma_share_mode_shared, NULL, NULL, &pStream);
23477  if (result != MA_SUCCESS) {
23478  return MA_FALSE;
23479  }
23480 
23481  ma_close_stream__aaudio(pContext, pStream);
23482  return MA_TRUE;
23483 }
23484 
23485 ma_result ma_wait_for_simple_state_transition__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_aaudio_stream_state_t oldState, ma_aaudio_stream_state_t newState)
23486 {
23487  ma_aaudio_stream_state_t actualNewState;
23488  ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000); /* 5 second timeout. */
23489  if (resultAA != MA_AAUDIO_OK) {
23490  return ma_result_from_aaudio(resultAA);
23491  }
23492 
23493  if (newState != actualNewState) {
23494  return MA_ERROR; /* Failed to transition into the expected state. */
23495  }
23496 
23497  return MA_SUCCESS;
23498 }
23499 
23500 
23501 ma_bool32 ma_context_is_device_id_equal__aaudio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
23502 {
23503  ma_assert(pContext != NULL);
23504  ma_assert(pID0 != NULL);
23505  ma_assert(pID1 != NULL);
23506  (void)pContext;
23507 
23508  return pID0->aaudio == pID1->aaudio;
23509 }
23510 
23511 ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
23512 {
23513  ma_bool32 cbResult = MA_TRUE;
23514 
23515  ma_assert(pContext != NULL);
23516  ma_assert(callback != NULL);
23517 
23518  /* Unfortunately AAudio does not have an enumeration API. Therefore I'm only going to report default devices, but only if it can instantiate a stream. */
23519 
23520  /* Playback. */
23521  if (cbResult) {
23522  ma_device_info deviceInfo;
23523  ma_zero_object(&deviceInfo);
23524  deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
23525  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
23526 
23527  if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) {
23528  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
23529  }
23530  }
23531 
23532  /* Capture. */
23533  if (cbResult) {
23534  ma_device_info deviceInfo;
23535  ma_zero_object(&deviceInfo);
23536  deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
23537  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
23538 
23539  if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) {
23540  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
23541  }
23542  }
23543 
23544  return MA_SUCCESS;
23545 }
23546 
23547 ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
23548 {
23549  ma_AAudioStream* pStream;
23550  ma_result result;
23551 
23552  ma_assert(pContext != NULL);
23553 
23554  /* No exclusive mode with AAudio. */
23555  if (shareMode == ma_share_mode_exclusive) {
23557  }
23558 
23559  /* ID */
23560  if (pDeviceID != NULL) {
23561  pDeviceInfo->id.aaudio = pDeviceID->aaudio;
23562  } else {
23563  pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED;
23564  }
23565 
23566  /* Name */
23567  if (deviceType == ma_device_type_playback) {
23568  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
23569  } else {
23570  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
23571  }
23572 
23573 
23574  /* We'll need to open the device to get accurate sample rate and channel count information. */
23575  result = ma_open_stream__aaudio(pContext, deviceType, pDeviceID, shareMode, NULL, NULL, &pStream);
23576  if (result != MA_SUCCESS) {
23577  return result;
23578  }
23579 
23580  pDeviceInfo->minChannels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream);
23581  pDeviceInfo->maxChannels = pDeviceInfo->minChannels;
23582  pDeviceInfo->minSampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream);
23583  pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
23584 
23585  ma_close_stream__aaudio(pContext, pStream);
23586  pStream = NULL;
23587 
23588 
23589  /* AAudio supports s16 and f32. */
23590  pDeviceInfo->formatCount = 2;
23591  pDeviceInfo->formats[0] = ma_format_s16;
23592  pDeviceInfo->formats[1] = ma_format_f32;
23593 
23594  return MA_SUCCESS;
23595 }
23596 
23597 
23598 void ma_device_uninit__aaudio(ma_device* pDevice)
23599 {
23600  ma_assert(pDevice != NULL);
23601 
23602  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23603  ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
23604  pDevice->aaudio.pStreamCapture = NULL;
23605  }
23606 
23607  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23608  ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
23609  pDevice->aaudio.pStreamPlayback = NULL;
23610  }
23611 
23612  if (pDevice->type == ma_device_type_duplex) {
23613  ma_pcm_rb_uninit(&pDevice->aaudio.duplexRB);
23614  }
23615 }
23616 
23617 ma_result ma_device_init__aaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
23618 {
23619  ma_result result;
23620 
23621  ma_assert(pDevice != NULL);
23622 
23623  if (pConfig->deviceType == ma_device_type_loopback) {
23625  }
23626 
23627  /* No exclusive mode with AAudio. */
23631  }
23632 
23633  /* We first need to try opening the stream. */
23634  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
23635  int32_t framesPerPeriod;
23636 
23637  result = ma_open_stream__aaudio(pContext, ma_device_type_capture, pConfig->capture.pDeviceID, pConfig->capture.shareMode, pConfig, pDevice, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture);
23638  if (result != MA_SUCCESS) {
23639  return result; /* Failed to open the AAudio stream. */
23640  }
23641 
23642  pDevice->capture.internalFormat = (((MA_PFN_AAudioStream_getFormat)pContext->aaudio.AAudioStream_getFormat)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32;
23643  pDevice->capture.internalChannels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
23644  pDevice->capture.internalSampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
23645  ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap); /* <-- Cannot find info on channel order, so assuming a default. */
23646  pDevice->capture.internalBufferSizeInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pContext->aaudio.AAudioStream_getBufferCapacityInFrames)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
23647 
23648  framesPerPeriod = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pContext->aaudio.AAudioStream_getFramesPerDataCallback)((ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
23649  if (framesPerPeriod > 0) {
23650  pDevice->capture.internalPeriods = pDevice->capture.internalBufferSizeInFrames / framesPerPeriod;
23651  } else {
23652  pDevice->capture.internalPeriods = 1;
23653  }
23654  }
23655 
23656  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
23657  int32_t framesPerPeriod;
23658 
23659  result = ma_open_stream__aaudio(pContext, ma_device_type_playback, pConfig->playback.pDeviceID, pConfig->playback.shareMode, pConfig, pDevice, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback);
23660  if (result != MA_SUCCESS) {
23661  return result; /* Failed to open the AAudio stream. */
23662  }
23663 
23664  pDevice->playback.internalFormat = (((MA_PFN_AAudioStream_getFormat)pContext->aaudio.AAudioStream_getFormat)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32;
23665  pDevice->playback.internalChannels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
23666  pDevice->playback.internalSampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
23667  ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap); /* <-- Cannot find info on channel order, so assuming a default. */
23668  pDevice->playback.internalBufferSizeInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pContext->aaudio.AAudioStream_getBufferCapacityInFrames)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
23669 
23670  framesPerPeriod = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pContext->aaudio.AAudioStream_getFramesPerDataCallback)((ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
23671  if (framesPerPeriod > 0) {
23672  pDevice->playback.internalPeriods = pDevice->playback.internalBufferSizeInFrames / framesPerPeriod;
23673  } else {
23674  pDevice->playback.internalPeriods = 1;
23675  }
23676  }
23677 
23678  if (pConfig->deviceType == ma_device_type_duplex) {
23679  ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_src(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalBufferSizeInFrames);
23680  ma_result result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->aaudio.duplexRB);
23681  if (result != MA_SUCCESS) {
23682  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23683  ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
23684  }
23685  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23686  ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
23687  }
23688  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[AAudio] Failed to initialize ring buffer.", result);
23689  }
23690 
23691  /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */
23692  {
23693  ma_uint32 marginSizeInFrames = rbSizeInFrames / pDevice->capture.internalPeriods;
23694  void* pMarginData;
23695  ma_pcm_rb_acquire_write(&pDevice->aaudio.duplexRB, &marginSizeInFrames, &pMarginData);
23696  {
23697  ma_zero_memory(pMarginData, marginSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
23698  }
23699  ma_pcm_rb_commit_write(&pDevice->aaudio.duplexRB, marginSizeInFrames, pMarginData);
23700  }
23701  }
23702 
23703  return MA_SUCCESS;
23704 }
23705 
23706 ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
23707 {
23708  ma_aaudio_result_t resultAA;
23709  ma_aaudio_stream_state_t currentState;
23710 
23711  ma_assert(pDevice != NULL);
23712 
23713  resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream);
23714  if (resultAA != MA_AAUDIO_OK) {
23715  return ma_result_from_aaudio(resultAA);
23716  }
23717 
23718  /* Do we actually need to wait for the device to transition into it's started state? */
23719 
23720  /* The device should be in either a starting or started state. If it's not set to started we need to wait for it to transition. It should go from starting to started. */
23721  currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
23722  if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) {
23723  ma_result result;
23724 
23725  if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) {
23726  return MA_ERROR; /* Expecting the stream to be a starting or started state. */
23727  }
23728 
23729  result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED);
23730  if (result != MA_SUCCESS) {
23731  return result;
23732  }
23733  }
23734 
23735  return MA_SUCCESS;
23736 }
23737 
23738 ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
23739 {
23740  ma_aaudio_result_t resultAA;
23741  ma_aaudio_stream_state_t currentState;
23742 
23743  ma_assert(pDevice != NULL);
23744 
23745  resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream);
23746  if (resultAA != MA_AAUDIO_OK) {
23747  return ma_result_from_aaudio(resultAA);
23748  }
23749 
23750  /* The device should be in either a stopping or stopped state. If it's not set to started we need to wait for it to transition. It should go from stopping to stopped. */
23751  currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
23752  if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) {
23753  ma_result result;
23754 
23755  if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) {
23756  return MA_ERROR; /* Expecting the stream to be a stopping or stopped state. */
23757  }
23758 
23759  result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED);
23760  if (result != MA_SUCCESS) {
23761  return result;
23762  }
23763  }
23764 
23765  return MA_SUCCESS;
23766 }
23767 
23768 ma_result ma_device_start__aaudio(ma_device* pDevice)
23769 {
23770  ma_assert(pDevice != NULL);
23771 
23772  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23773  ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
23774  if (result != MA_SUCCESS) {
23775  return result;
23776  }
23777  }
23778 
23779  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23780  ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
23781  if (result != MA_SUCCESS) {
23782  if (pDevice->type == ma_device_type_duplex) {
23783  ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
23784  }
23785  return result;
23786  }
23787  }
23788 
23789  return MA_SUCCESS;
23790 }
23791 
23792 ma_result ma_device_stop__aaudio(ma_device* pDevice)
23793 {
23794  ma_stop_proc onStop;
23795 
23796  ma_assert(pDevice != NULL);
23797 
23798  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
23799  ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
23800  if (result != MA_SUCCESS) {
23801  return result;
23802  }
23803  }
23804 
23805  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
23806  ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
23807  if (result != MA_SUCCESS) {
23808  return result;
23809  }
23810  }
23811 
23812  onStop = pDevice->onStop;
23813  if (onStop) {
23814  onStop(pDevice);
23815  }
23816 
23817  return MA_SUCCESS;
23818 }
23819 
23820 
23821 ma_result ma_context_uninit__aaudio(ma_context* pContext)
23822 {
23823  ma_assert(pContext != NULL);
23824  ma_assert(pContext->backend == ma_backend_aaudio);
23825 
23826  ma_dlclose(pContext, pContext->aaudio.hAAudio);
23827  pContext->aaudio.hAAudio = NULL;
23828 
23829  return MA_SUCCESS;
23830 }
23831 
23832 ma_result ma_context_init__aaudio(const ma_context_config* pConfig, ma_context* pContext)
23833 {
23834  const char* libNames[] = {
23835  "libaaudio.so"
23836  };
23837  size_t i;
23838 
23839  for (i = 0; i < ma_countof(libNames); ++i) {
23840  pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]);
23841  if (pContext->aaudio.hAAudio != NULL) {
23842  break;
23843  }
23844  }
23845 
23846  if (pContext->aaudio.hAAudio == NULL) {
23848  }
23849 
23850  pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudio_createStreamBuilder");
23851  pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete");
23852  pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId");
23853  pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection");
23854  pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode");
23855  pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat");
23856  pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount");
23857  pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate");
23858  pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames");
23859  pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback");
23860  pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback");
23861  pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode");
23862  pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream");
23863  pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close");
23864  pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState");
23865  pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange");
23866  pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat");
23867  pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount");
23868  pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate");
23869  pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames");
23870  pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback");
23871  pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst");
23872  pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart");
23873  pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop");
23874 
23875  pContext->isBackendAsynchronous = MA_TRUE;
23876 
23877  pContext->onUninit = ma_context_uninit__aaudio;
23878  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__aaudio;
23879  pContext->onEnumDevices = ma_context_enumerate_devices__aaudio;
23880  pContext->onGetDeviceInfo = ma_context_get_device_info__aaudio;
23881  pContext->onDeviceInit = ma_device_init__aaudio;
23882  pContext->onDeviceUninit = ma_device_uninit__aaudio;
23883  pContext->onDeviceStart = ma_device_start__aaudio;
23884  pContext->onDeviceStop = ma_device_stop__aaudio;
23885 
23886  (void)pConfig;
23887  return MA_SUCCESS;
23888 }
23889 #endif /* AAudio */
23890 
23891 
23892 /******************************************************************************
23893 
23894 OpenSL|ES Backend
23895 
23896 ******************************************************************************/
23897 #ifdef MA_HAS_OPENSL
23898 #include <SLES/OpenSLES.h>
23899 #ifdef MA_ANDROID
23900 #include <SLES/OpenSLES_Android.h>
23901 #endif
23902 
23903 /* OpenSL|ES has one-per-application objects :( */
23904 SLObjectItf g_maEngineObjectSL = NULL;
23905 SLEngineItf g_maEngineSL = NULL;
23906 ma_uint32 g_maOpenSLInitCounter = 0;
23907 
23908 #define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p)))
23909 #define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p)))
23910 #define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p)))
23911 #define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p)))
23912 
23913 #ifdef MA_ANDROID
23914 #define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p)))
23915 #else
23916 #define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p)))
23917 #endif
23918 
23919 /* Converts an individual OpenSL-style channel identifier (SL_SPEAKER_FRONT_LEFT, etc.) to miniaudio. */
23920 ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id)
23921 {
23922  switch (id)
23923  {
23924  case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
23925  case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
23926  case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
23927  case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
23928  case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
23929  case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
23930  case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
23931  case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
23932  case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
23933  case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
23934  case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
23935  case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
23936  case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
23937  case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
23938  case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
23939  case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
23940  case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
23941  case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
23942  default: return 0;
23943  }
23944 }
23945 
23946 /* Converts an individual miniaudio channel identifier (MA_CHANNEL_FRONT_LEFT, etc.) to OpenSL-style. */
23947 SLuint32 ma_channel_id_to_opensl(ma_uint8 id)
23948 {
23949  switch (id)
23950  {
23951  case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER;
23952  case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT;
23953  case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT;
23954  case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER;
23955  case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY;
23956  case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT;
23957  case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT;
23958  case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER;
23959  case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER;
23960  case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER;
23961  case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT;
23962  case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT;
23963  case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER;
23964  case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT;
23965  case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER;
23966  case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT;
23967  case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT;
23968  case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER;
23969  case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT;
23970  default: return 0;
23971  }
23972 }
23973 
23974 /* Converts a channel mapping to an OpenSL-style channel mask. */
23975 SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel channelMap[MA_MAX_CHANNELS], ma_uint32 channels)
23976 {
23977  SLuint32 channelMask = 0;
23978  ma_uint32 iChannel;
23979  for (iChannel = 0; iChannel < channels; ++iChannel) {
23980  channelMask |= ma_channel_id_to_opensl(channelMap[iChannel]);
23981  }
23982 
23983  return channelMask;
23984 }
23985 
23986 /* Converts an OpenSL-style channel mask to a miniaudio channel map. */
23987 void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
23988 {
23989  if (channels == 1 && channelMask == 0) {
23990  channelMap[0] = MA_CHANNEL_MONO;
23991  } else if (channels == 2 && channelMask == 0) {
23992  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
23993  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
23994  } else {
23995  if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) {
23996  channelMap[0] = MA_CHANNEL_MONO;
23997  } else {
23998  /* Just iterate over each bit. */
23999  ma_uint32 iChannel = 0;
24000  ma_uint32 iBit;
24001  for (iBit = 0; iBit < 32; ++iBit) {
24002  SLuint32 bitValue = (channelMask & (1UL << iBit));
24003  if (bitValue != 0) {
24004  /* The bit is set. */
24005  channelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue);
24006  iChannel += 1;
24007  }
24008  }
24009  }
24010  }
24011 }
24012 
24013 SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec)
24014 {
24015  if (samplesPerSec <= SL_SAMPLINGRATE_8) {
24016  return SL_SAMPLINGRATE_8;
24017  }
24018  if (samplesPerSec <= SL_SAMPLINGRATE_11_025) {
24019  return SL_SAMPLINGRATE_11_025;
24020  }
24021  if (samplesPerSec <= SL_SAMPLINGRATE_12) {
24022  return SL_SAMPLINGRATE_12;
24023  }
24024  if (samplesPerSec <= SL_SAMPLINGRATE_16) {
24025  return SL_SAMPLINGRATE_16;
24026  }
24027  if (samplesPerSec <= SL_SAMPLINGRATE_22_05) {
24028  return SL_SAMPLINGRATE_22_05;
24029  }
24030  if (samplesPerSec <= SL_SAMPLINGRATE_24) {
24031  return SL_SAMPLINGRATE_24;
24032  }
24033  if (samplesPerSec <= SL_SAMPLINGRATE_32) {
24034  return SL_SAMPLINGRATE_32;
24035  }
24036  if (samplesPerSec <= SL_SAMPLINGRATE_44_1) {
24037  return SL_SAMPLINGRATE_44_1;
24038  }
24039  if (samplesPerSec <= SL_SAMPLINGRATE_48) {
24040  return SL_SAMPLINGRATE_48;
24041  }
24042 
24043  /* Android doesn't support more than 48000. */
24044 #ifndef MA_ANDROID
24045  if (samplesPerSec <= SL_SAMPLINGRATE_64) {
24046  return SL_SAMPLINGRATE_64;
24047  }
24048  if (samplesPerSec <= SL_SAMPLINGRATE_88_2) {
24049  return SL_SAMPLINGRATE_88_2;
24050  }
24051  if (samplesPerSec <= SL_SAMPLINGRATE_96) {
24052  return SL_SAMPLINGRATE_96;
24053  }
24054  if (samplesPerSec <= SL_SAMPLINGRATE_192) {
24055  return SL_SAMPLINGRATE_192;
24056  }
24057 #endif
24058 
24059  return SL_SAMPLINGRATE_16;
24060 }
24061 
24062 
24063 ma_bool32 ma_context_is_device_id_equal__opensl(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
24064 {
24065  ma_assert(pContext != NULL);
24066  ma_assert(pID0 != NULL);
24067  ma_assert(pID1 != NULL);
24068  (void)pContext;
24069 
24070  return pID0->opensl == pID1->opensl;
24071 }
24072 
24073 ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
24074 {
24075  ma_bool32 cbResult;
24076 
24077  ma_assert(pContext != NULL);
24078  ma_assert(callback != NULL);
24079 
24080  ma_assert(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to enumerate devices. */
24081  if (g_maOpenSLInitCounter == 0) {
24082  return MA_INVALID_OPERATION;
24083  }
24084 
24085  /*
24086  TODO: Test Me.
24087 
24088  This is currently untested, so for now we are just returning default devices.
24089  */
24090 #if 0 && !defined(MA_ANDROID)
24091  ma_bool32 isTerminated = MA_FALSE;
24092 
24093  SLuint32 pDeviceIDs[128];
24094  SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]);
24095 
24096  SLAudioIODeviceCapabilitiesItf deviceCaps;
24097  SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
24098  if (resultSL != SL_RESULT_SUCCESS) {
24099  /* The interface may not be supported so just report a default device. */
24100  goto return_default_device;
24101  }
24102 
24103  /* Playback */
24104  if (!isTerminated) {
24105  resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs);
24106  if (resultSL != SL_RESULT_SUCCESS) {
24107  return MA_NO_DEVICE;
24108  }
24109 
24110  for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
24111  ma_device_info deviceInfo;
24112  ma_zero_object(&deviceInfo);
24113  deviceInfo.id.opensl = pDeviceIDs[iDevice];
24114 
24115  SLAudioOutputDescriptor desc;
24116  resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
24117  if (resultSL == SL_RESULT_SUCCESS) {
24118  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1);
24119 
24120  ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
24121  if (cbResult == MA_FALSE) {
24122  isTerminated = MA_TRUE;
24123  break;
24124  }
24125  }
24126  }
24127  }
24128 
24129  /* Capture */
24130  if (!isTerminated) {
24131  resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs);
24132  if (resultSL != SL_RESULT_SUCCESS) {
24133  return MA_NO_DEVICE;
24134  }
24135 
24136  for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
24137  ma_device_info deviceInfo;
24138  ma_zero_object(&deviceInfo);
24139  deviceInfo.id.opensl = pDeviceIDs[iDevice];
24140 
24141  SLAudioInputDescriptor desc;
24142  resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
24143  if (resultSL == SL_RESULT_SUCCESS) {
24144  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1);
24145 
24146  ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
24147  if (cbResult == MA_FALSE) {
24148  isTerminated = MA_TRUE;
24149  break;
24150  }
24151  }
24152  }
24153  }
24154 
24155  return MA_SUCCESS;
24156 #else
24157  goto return_default_device;
24158 #endif
24159 
24160 return_default_device:;
24161  cbResult = MA_TRUE;
24162 
24163  /* Playback. */
24164  if (cbResult) {
24165  ma_device_info deviceInfo;
24166  ma_zero_object(&deviceInfo);
24167  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
24168  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
24169  }
24170 
24171  /* Capture. */
24172  if (cbResult) {
24173  ma_device_info deviceInfo;
24174  ma_zero_object(&deviceInfo);
24175  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
24176  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
24177  }
24178 
24179  return MA_SUCCESS;
24180 }
24181 
24182 ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
24183 {
24184  ma_assert(pContext != NULL);
24185 
24186  ma_assert(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to get device info. */
24187  if (g_maOpenSLInitCounter == 0) {
24188  return MA_INVALID_OPERATION;
24189  }
24190 
24191  /* No exclusive mode with OpenSL|ES. */
24192  if (shareMode == ma_share_mode_exclusive) {
24194  }
24195 
24196  /*
24197  TODO: Test Me.
24198 
24199  This is currently untested, so for now we are just returning default devices.
24200  */
24201 #if 0 && !defined(MA_ANDROID)
24202  SLAudioIODeviceCapabilitiesItf deviceCaps;
24203  SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
24204  if (resultSL != SL_RESULT_SUCCESS) {
24205  /* The interface may not be supported so just report a default device. */
24206  goto return_default_device;
24207  }
24208 
24209  if (deviceType == ma_device_type_playback) {
24210  SLAudioOutputDescriptor desc;
24211  resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
24212  if (resultSL != SL_RESULT_SUCCESS) {
24213  return MA_NO_DEVICE;
24214  }
24215 
24216  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1);
24217  } else {
24218  SLAudioInputDescriptor desc;
24219  resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
24220  if (resultSL != SL_RESULT_SUCCESS) {
24221  return MA_NO_DEVICE;
24222  }
24223 
24224  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1);
24225  }
24226 
24227  goto return_detailed_info;
24228 #else
24229  goto return_default_device;
24230 #endif
24231 
24232 return_default_device:
24233  if (pDeviceID != NULL) {
24234  if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) ||
24235  (deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) {
24236  return MA_NO_DEVICE; /* Don't know the device. */
24237  }
24238  }
24239 
24240  /* Name / Description */
24241  if (deviceType == ma_device_type_playback) {
24242  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
24243  } else {
24244  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
24245  }
24246 
24247  goto return_detailed_info;
24248 
24249 
24250 return_detailed_info:
24251 
24252  /*
24253  For now we're just outputting a set of values that are supported by the API but not necessarily supported
24254  by the device natively. Later on we should work on this so that it more closely reflects the device's
24255  actual native format.
24256  */
24257  pDeviceInfo->minChannels = 1;
24258  pDeviceInfo->maxChannels = 2;
24259  pDeviceInfo->minSampleRate = 8000;
24260  pDeviceInfo->maxSampleRate = 48000;
24261  pDeviceInfo->formatCount = 2;
24262  pDeviceInfo->formats[0] = ma_format_u8;
24263  pDeviceInfo->formats[1] = ma_format_s16;
24264 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
24265  pDeviceInfo->formats[pDeviceInfo->formatCount] = ma_format_f32;
24266  pDeviceInfo->formatCount += 1;
24267 #endif
24268 
24269  return MA_SUCCESS;
24270 }
24271 
24272 
24273 #ifdef MA_ANDROID
24274 /*void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, SLuint32 eventFlags, const void* pBuffer, SLuint32 bufferSize, SLuint32 dataUsed, void* pContext)*/
24275 void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
24276 {
24277  ma_device* pDevice = (ma_device*)pUserData;
24278  size_t periodSizeInBytes;
24279  ma_uint8* pBuffer;
24280  SLresult resultSL;
24281 
24282  ma_assert(pDevice != NULL);
24283 
24284  (void)pBufferQueue;
24285 
24286  /*
24287  For now, don't do anything unless the buffer was fully processed. From what I can tell, it looks like
24288  OpenSL|ES 1.1 improves on buffer queues to the point that we could much more intelligently handle this,
24289  but unfortunately it looks like Android is only supporting OpenSL|ES 1.0.1 for now :(
24290  */
24291 
24292  /* Don't do anything if the device is not started. */
24293  if (pDevice->state != MA_STATE_STARTED) {
24294  return;
24295  }
24296 
24297  periodSizeInBytes = (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods) * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
24298  pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes);
24299 
24300  if (pDevice->type == ma_device_type_duplex) {
24301  ma_device__handle_duplex_callback_capture(pDevice, (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods), pBuffer, &pDevice->opensl.duplexRB);
24302  } else {
24303  ma_device__send_frames_to_client(pDevice, (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods), pBuffer);
24304  }
24305 
24306  resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes);
24307  if (resultSL != SL_RESULT_SUCCESS) {
24308  return;
24309  }
24310 
24311  pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods;
24312 }
24313 
24314 void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
24315 {
24316  ma_device* pDevice = (ma_device*)pUserData;
24317  size_t periodSizeInBytes;
24318  ma_uint8* pBuffer;
24319  SLresult resultSL;
24320 
24321  ma_assert(pDevice != NULL);
24322 
24323  (void)pBufferQueue;
24324 
24325  /* Don't do anything if the device is not started. */
24326  if (pDevice->state != MA_STATE_STARTED) {
24327  return;
24328  }
24329 
24330  periodSizeInBytes = (pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods) * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
24331  pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes);
24332 
24333  if (pDevice->type == ma_device_type_duplex) {
24334  ma_device__handle_duplex_callback_playback(pDevice, (pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods), pBuffer, &pDevice->opensl.duplexRB);
24335  } else {
24336  ma_device__read_frames_from_client(pDevice, (pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods), pBuffer);
24337  }
24338 
24339  resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes);
24340  if (resultSL != SL_RESULT_SUCCESS) {
24341  return;
24342  }
24343 
24344  pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods;
24345 }
24346 #endif
24347 
24348 void ma_device_uninit__opensl(ma_device* pDevice)
24349 {
24350  ma_assert(pDevice != NULL);
24351 
24352  ma_assert(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before uninitializing the device. */
24353  if (g_maOpenSLInitCounter == 0) {
24354  return;
24355  }
24356 
24357  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24358  if (pDevice->opensl.pAudioRecorderObj) {
24359  MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj);
24360  }
24361 
24362  ma_free(pDevice->opensl.pBufferCapture);
24363  }
24364 
24365  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24366  if (pDevice->opensl.pAudioPlayerObj) {
24367  MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj);
24368  }
24369  if (pDevice->opensl.pOutputMixObj) {
24370  MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj);
24371  }
24372 
24373  ma_free(pDevice->opensl.pBufferPlayback);
24374  }
24375 
24376  if (pDevice->type == ma_device_type_duplex) {
24377  ma_pcm_rb_uninit(&pDevice->opensl.duplexRB);
24378  }
24379 }
24380 
24381 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
24382 typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM;
24383 #else
24384 typedef SLDataFormat_PCM ma_SLDataFormat_PCM;
24385 #endif
24386 
24387 ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat)
24388 {
24389 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
24390  if (format == ma_format_f32) {
24391  pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
24392  pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
24393  } else {
24394  pDataFormat->formatType = SL_DATAFORMAT_PCM;
24395  }
24396 #else
24397  pDataFormat->formatType = SL_DATAFORMAT_PCM;
24398 #endif
24399 
24400  pDataFormat->numChannels = channels;
24401  ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate * 1000); /* In millihertz. Annoyingly, the sample rate variable is named differently between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM */
24402  pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format)*8;
24403  pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels);
24404  pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
24405 
24406  /*
24407  Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html
24408  - Only mono and stereo is supported.
24409  - Only u8 and s16 formats are supported.
24410  - Maximum sample rate of 48000.
24411  */
24412 #ifdef MA_ANDROID
24413  if (pDataFormat->numChannels > 2) {
24414  pDataFormat->numChannels = 2;
24415  }
24416 #if __ANDROID_API__ >= 21
24417  if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
24418  /* It's floating point. */
24419  ma_assert(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
24420  if (pDataFormat->bitsPerSample > 32) {
24421  pDataFormat->bitsPerSample = 32;
24422  }
24423  } else {
24424  if (pDataFormat->bitsPerSample > 16) {
24425  pDataFormat->bitsPerSample = 16;
24426  }
24427  }
24428 #else
24429  if (pDataFormat->bitsPerSample > 16) {
24430  pDataFormat->bitsPerSample = 16;
24431  }
24432 #endif
24433  if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) {
24434  ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48;
24435  }
24436 #endif
24437 
24438  pDataFormat->containerSize = pDataFormat->bitsPerSample; /* Always tightly packed for now. */
24439 
24440  return MA_SUCCESS;
24441 }
24442 
24443 ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap)
24444 {
24445  ma_bool32 isFloatingPoint = MA_FALSE;
24446 #if defined(MA_ANDROID) && __ANDROID_API__ >= 21
24447  if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
24448  ma_assert(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
24449  isFloatingPoint = MA_TRUE;
24450  }
24451 #endif
24452  if (isFloatingPoint) {
24453  if (pDataFormat->bitsPerSample == 32) {
24454  *pFormat = ma_format_f32;
24455  }
24456  } else {
24457  if (pDataFormat->bitsPerSample == 8) {
24458  *pFormat = ma_format_u8;
24459  } else if (pDataFormat->bitsPerSample == 16) {
24460  *pFormat = ma_format_s16;
24461  } else if (pDataFormat->bitsPerSample == 24) {
24462  *pFormat = ma_format_s24;
24463  } else if (pDataFormat->bitsPerSample == 32) {
24464  *pFormat = ma_format_s32;
24465  }
24466  }
24467 
24468  *pChannels = pDataFormat->numChannels;
24469  *pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000;
24470  ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, pDataFormat->numChannels, pChannelMap);
24471 
24472  return MA_SUCCESS;
24473 }
24474 
24475 ma_result ma_device_init__opensl(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
24476 {
24477 #ifdef MA_ANDROID
24478  SLDataLocator_AndroidSimpleBufferQueue queue;
24479  SLresult resultSL;
24480  ma_uint32 bufferSizeInFrames;
24481  size_t bufferSizeInBytes;
24482  const SLInterfaceID itfIDs1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
24483  const SLboolean itfIDsRequired1[] = {SL_BOOLEAN_TRUE};
24484 #endif
24485 
24486  (void)pContext;
24487 
24488  ma_assert(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to initialize a new device. */
24489  if (g_maOpenSLInitCounter == 0) {
24490  return MA_INVALID_OPERATION;
24491  }
24492 
24493  if (pConfig->deviceType == ma_device_type_loopback) {
24495  }
24496 
24497  /*
24498  For now, only supporting Android implementations of OpenSL|ES since that's the only one I've
24499  been able to test with and I currently depend on Android-specific extensions (simple buffer
24500  queues).
24501  */
24502 #ifdef MA_ANDROID
24503  /* No exclusive mode with OpenSL|ES. */
24507  }
24508 
24509  /* Now we can start initializing the device properly. */
24510  ma_assert(pDevice != NULL);
24511  ma_zero_object(&pDevice->opensl);
24512 
24513  queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
24514  queue.numBuffers = pConfig->periods;
24515 
24516 
24517  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
24518  ma_SLDataFormat_PCM pcm;
24519  SLDataLocator_IODevice locatorDevice;
24520  SLDataSource source;
24521  SLDataSink sink;
24522 
24523  ma_SLDataFormat_PCM_init__opensl(pConfig->capture.format, pConfig->capture.channels, pConfig->sampleRate, pConfig->capture.channelMap, &pcm);
24524 
24525  locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE;
24526  locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT;
24527  locatorDevice.deviceID = (pConfig->capture.pDeviceID == NULL) ? SL_DEFAULTDEVICEID_AUDIOINPUT : pConfig->capture.pDeviceID->opensl;
24528  locatorDevice.device = NULL;
24529 
24530  source.pLocator = &locatorDevice;
24531  source.pFormat = NULL;
24532 
24533  sink.pLocator = &queue;
24534  sink.pFormat = (SLDataFormat_PCM*)&pcm;
24535 
24536  resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
24537  if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) {
24538  /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
24539  pcm.formatType = SL_DATAFORMAT_PCM;
24540  pcm.numChannels = 1;
24541  ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16; /* The name of the sample rate variable is different between SLAndroidDataFormat_PCM_EX and SLDataFormat_PCM. */
24542  pcm.bitsPerSample = 16;
24543  pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
24544  pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
24545  resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
24546  }
24547 
24548  if (resultSL != SL_RESULT_SUCCESS) {
24549  ma_device_uninit__opensl(pDevice);
24550  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24551  }
24552 
24553  if (MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE) != SL_RESULT_SUCCESS) {
24554  ma_device_uninit__opensl(pDevice);
24555  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24556  }
24557 
24558  if (MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_RECORD, &pDevice->opensl.pAudioRecorder) != SL_RESULT_SUCCESS) {
24559  ma_device_uninit__opensl(pDevice);
24560  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24561  }
24562 
24563  if (MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture) != SL_RESULT_SUCCESS) {
24564  ma_device_uninit__opensl(pDevice);
24565  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24566  }
24567 
24568  if (MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice) != SL_RESULT_SUCCESS) {
24569  ma_device_uninit__opensl(pDevice);
24570  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24571  }
24572 
24573  /* The internal format is determined by the "pcm" object. */
24574  ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDevice->capture.internalFormat, &pDevice->capture.internalChannels, &pDevice->capture.internalSampleRate, pDevice->capture.internalChannelMap);
24575 
24576  /* Buffer. */
24577  bufferSizeInFrames = pConfig->bufferSizeInFrames;
24578  if (bufferSizeInFrames == 0) {
24579  bufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, pDevice->capture.internalSampleRate);
24580  }
24581  pDevice->capture.internalPeriods = pConfig->periods;
24582  pDevice->capture.internalBufferSizeInFrames = (bufferSizeInFrames / pDevice->capture.internalPeriods) * pDevice->capture.internalPeriods;
24583  pDevice->opensl.currentBufferIndexCapture = 0;
24584 
24585  bufferSizeInBytes = pDevice->capture.internalBufferSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
24586  pDevice->opensl.pBufferCapture = (ma_uint8*)ma_malloc(bufferSizeInBytes);
24587  if (pDevice->opensl.pBufferCapture == NULL) {
24588  ma_device_uninit__opensl(pDevice);
24589  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MA_OUT_OF_MEMORY);
24590  }
24591  MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes);
24592  }
24593 
24594  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
24595  ma_SLDataFormat_PCM pcm;
24596  SLDataSource source;
24597  SLDataLocator_OutputMix outmixLocator;
24598  SLDataSink sink;
24599 
24600  ma_SLDataFormat_PCM_init__opensl(pConfig->playback.format, pConfig->playback.channels, pConfig->sampleRate, pConfig->playback.channelMap, &pcm);
24601 
24602  resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL);
24603  if (resultSL != SL_RESULT_SUCCESS) {
24604  ma_device_uninit__opensl(pDevice);
24605  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24606  }
24607 
24608  if (MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE)) {
24609  ma_device_uninit__opensl(pDevice);
24610  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24611  }
24612 
24613  if (MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix) != SL_RESULT_SUCCESS) {
24614  ma_device_uninit__opensl(pDevice);
24615  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24616  }
24617 
24618  /* Set the output device. */
24619  if (pConfig->playback.pDeviceID != NULL) {
24620  SLuint32 deviceID_OpenSL = pConfig->playback.pDeviceID->opensl;
24621  MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL);
24622  }
24623 
24624  source.pLocator = &queue;
24625  source.pFormat = (SLDataFormat_PCM*)&pcm;
24626 
24627  outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
24628  outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj;
24629 
24630  sink.pLocator = &outmixLocator;
24631  sink.pFormat = NULL;
24632 
24633  resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
24634  if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED) {
24635  /* Unsupported format. Fall back to something safer and try again. If this fails, just abort. */
24636  pcm.formatType = SL_DATAFORMAT_PCM;
24637  pcm.numChannels = 2;
24638  ((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;
24639  pcm.bitsPerSample = 16;
24640  pcm.containerSize = pcm.bitsPerSample; /* Always tightly packed for now. */
24641  pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
24642  resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, 1, itfIDs1, itfIDsRequired1);
24643  }
24644 
24645  if (resultSL != SL_RESULT_SUCCESS) {
24646  ma_device_uninit__opensl(pDevice);
24647  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24648  }
24649 
24650  if (MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE) != SL_RESULT_SUCCESS) {
24651  ma_device_uninit__opensl(pDevice);
24652  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24653  }
24654 
24655  if (MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_PLAY, &pDevice->opensl.pAudioPlayer) != SL_RESULT_SUCCESS) {
24656  ma_device_uninit__opensl(pDevice);
24657  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24658  }
24659 
24660  if (MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback) != SL_RESULT_SUCCESS) {
24661  ma_device_uninit__opensl(pDevice);
24662  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24663  }
24664 
24665  if (MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice) != SL_RESULT_SUCCESS) {
24666  ma_device_uninit__opensl(pDevice);
24667  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.", MA_FAILED_TO_OPEN_BACKEND_DEVICE);
24668  }
24669 
24670  /* The internal format is determined by the "pcm" object. */
24671  ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDevice->playback.internalFormat, &pDevice->playback.internalChannels, &pDevice->playback.internalSampleRate, pDevice->playback.internalChannelMap);
24672 
24673  /* Buffer. */
24674  bufferSizeInFrames = pConfig->bufferSizeInFrames;
24675  if (bufferSizeInFrames == 0) {
24676  bufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, pDevice->playback.internalSampleRate);
24677  }
24678  pDevice->playback.internalPeriods = pConfig->periods;
24679  pDevice->playback.internalBufferSizeInFrames = (bufferSizeInFrames / pDevice->playback.internalPeriods) * pDevice->playback.internalPeriods;
24680  pDevice->opensl.currentBufferIndexPlayback = 0;
24681 
24682  bufferSizeInBytes = pDevice->playback.internalBufferSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
24683  pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_malloc(bufferSizeInBytes);
24684  if (pDevice->opensl.pBufferPlayback == NULL) {
24685  ma_device_uninit__opensl(pDevice);
24686  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.", MA_OUT_OF_MEMORY);
24687  }
24688  MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes);
24689  }
24690 
24691  if (pConfig->deviceType == ma_device_type_duplex) {
24692  ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_src(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalBufferSizeInFrames);
24693  ma_result result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->opensl.duplexRB);
24694  if (result != MA_SUCCESS) {
24695  ma_device_uninit__opensl(pDevice);
24696  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to initialize ring buffer.", result);
24697  }
24698 
24699  /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */
24700  {
24701  ma_uint32 marginSizeInFrames = rbSizeInFrames / pDevice->capture.internalPeriods;
24702  void* pMarginData;
24703  ma_pcm_rb_acquire_write(&pDevice->opensl.duplexRB, &marginSizeInFrames, &pMarginData);
24704  {
24705  ma_zero_memory(pMarginData, marginSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
24706  }
24707  ma_pcm_rb_commit_write(&pDevice->opensl.duplexRB, marginSizeInFrames, pMarginData);
24708  }
24709  }
24710 
24711  return MA_SUCCESS;
24712 #else
24713  return MA_NO_BACKEND; /* Non-Android implementations are not supported. */
24714 #endif
24715 }
24716 
24717 ma_result ma_device_start__opensl(ma_device* pDevice)
24718 {
24719  SLresult resultSL;
24720  size_t periodSizeInBytes;
24721  ma_uint32 iPeriod;
24722 
24723  ma_assert(pDevice != NULL);
24724 
24725  ma_assert(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it and then attempted to start the device. */
24726  if (g_maOpenSLInitCounter == 0) {
24727  return MA_INVALID_OPERATION;
24728  }
24729 
24730  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24731  resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING);
24732  if (resultSL != SL_RESULT_SUCCESS) {
24733  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device.", MA_FAILED_TO_START_BACKEND_DEVICE);
24734  }
24735 
24736  periodSizeInBytes = (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods) * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
24737  for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
24738  resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes);
24739  if (resultSL != SL_RESULT_SUCCESS) {
24740  MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
24741  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device.", MA_FAILED_TO_START_BACKEND_DEVICE);
24742  }
24743  }
24744  }
24745 
24746  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24747  resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING);
24748  if (resultSL != SL_RESULT_SUCCESS) {
24749  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device.", MA_FAILED_TO_START_BACKEND_DEVICE);
24750  }
24751 
24752  /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueu silent buffers. */
24753  if (pDevice->type == ma_device_type_duplex) {
24754  MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalBufferSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
24755  } else {
24756  ma_device__read_frames_from_client(pDevice, pDevice->playback.internalBufferSizeInFrames, pDevice->opensl.pBufferPlayback);
24757  }
24758 
24759  periodSizeInBytes = (pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods) * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
24760  for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
24761  resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes);
24762  if (resultSL != SL_RESULT_SUCCESS) {
24763  MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
24764  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device.", MA_FAILED_TO_START_BACKEND_DEVICE);
24765  }
24766  }
24767  }
24768 
24769  return MA_SUCCESS;
24770 }
24771 
24772 ma_result ma_device_stop__opensl(ma_device* pDevice)
24773 {
24774  SLresult resultSL;
24775  ma_stop_proc onStop;
24776 
24777  ma_assert(pDevice != NULL);
24778 
24779  ma_assert(g_maOpenSLInitCounter > 0); /* <-- If you trigger this it means you've either not initialized the context, or you've uninitialized it before stopping/uninitializing the device. */
24780  if (g_maOpenSLInitCounter == 0) {
24781  return MA_INVALID_OPERATION;
24782  }
24783 
24784  /* TODO: Wait until all buffers have been processed. Hint: Maybe SLAndroidSimpleBufferQueue::GetState() could be used in a loop? */
24785 
24786  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
24787  resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
24788  if (resultSL != SL_RESULT_SUCCESS) {
24789  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
24790  }
24791 
24792  MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture);
24793  }
24794 
24795  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
24796  resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
24797  if (resultSL != SL_RESULT_SUCCESS) {
24798  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device.", MA_FAILED_TO_STOP_BACKEND_DEVICE);
24799  }
24800 
24801  MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback);
24802  }
24803 
24804  /* Make sure the client is aware that the device has stopped. There may be an OpenSL|ES callback for this, but I haven't found it. */
24805  onStop = pDevice->onStop;
24806  if (onStop) {
24807  onStop(pDevice);
24808  }
24809 
24810  return MA_SUCCESS;
24811 }
24812 
24813 
24814 ma_result ma_context_uninit__opensl(ma_context* pContext)
24815 {
24816  ma_assert(pContext != NULL);
24817  ma_assert(pContext->backend == ma_backend_opensl);
24818  (void)pContext;
24819 
24820  /* Uninit global data. */
24821  if (g_maOpenSLInitCounter > 0) {
24822  if (ma_atomic_decrement_32(&g_maOpenSLInitCounter) == 0) {
24823  (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
24824  }
24825  }
24826 
24827  return MA_SUCCESS;
24828 }
24829 
24830 ma_result ma_context_init__opensl(const ma_context_config* pConfig, ma_context* pContext)
24831 {
24832  ma_assert(pContext != NULL);
24833 
24834  (void)pConfig;
24835 
24836  /* Initialize global data first if applicable. */
24837  if (ma_atomic_increment_32(&g_maOpenSLInitCounter) == 1) {
24838  SLresult resultSL = slCreateEngine(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL);
24839  if (resultSL != SL_RESULT_SUCCESS) {
24840  ma_atomic_decrement_32(&g_maOpenSLInitCounter);
24841  return MA_NO_BACKEND;
24842  }
24843 
24844  (*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE);
24845 
24846  resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, SL_IID_ENGINE, &g_maEngineSL);
24847  if (resultSL != SL_RESULT_SUCCESS) {
24848  (*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
24849  ma_atomic_decrement_32(&g_maOpenSLInitCounter);
24850  return MA_NO_BACKEND;
24851  }
24852  }
24853 
24854  pContext->isBackendAsynchronous = MA_TRUE;
24855 
24856  pContext->onUninit = ma_context_uninit__opensl;
24857  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__opensl;
24858  pContext->onEnumDevices = ma_context_enumerate_devices__opensl;
24859  pContext->onGetDeviceInfo = ma_context_get_device_info__opensl;
24860  pContext->onDeviceInit = ma_device_init__opensl;
24861  pContext->onDeviceUninit = ma_device_uninit__opensl;
24862  pContext->onDeviceStart = ma_device_start__opensl;
24863  pContext->onDeviceStop = ma_device_stop__opensl;
24864 
24865  return MA_SUCCESS;
24866 }
24867 #endif /* OpenSL|ES */
24868 
24869 
24870 /******************************************************************************
24871 
24872 Web Audio Backend
24873 
24874 ******************************************************************************/
24875 #ifdef MA_HAS_WEBAUDIO
24876 #include <emscripten/emscripten.h>
24877 
24878 ma_bool32 ma_is_capture_supported__webaudio()
24879 {
24880  return EM_ASM_INT({
24881  return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined);
24882  }, 0) != 0; /* Must pass in a dummy argument for C99 compatibility. */
24883 }
24884 
24885 #ifdef __cplusplus
24886 extern "C" {
24887 #endif
24888 EMSCRIPTEN_KEEPALIVE void ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
24889 {
24890  if (pDevice->type == ma_device_type_duplex) {
24891  ma_device__handle_duplex_callback_capture(pDevice, (ma_uint32)frameCount, pFrames, &pDevice->webaudio.duplexRB);
24892  } else {
24893  ma_device__send_frames_to_client(pDevice, (ma_uint32)frameCount, pFrames); /* Send directly to the client. */
24894  }
24895 }
24896 
24897 EMSCRIPTEN_KEEPALIVE void ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
24898 {
24899  if (pDevice->type == ma_device_type_duplex) {
24900  ma_device__handle_duplex_callback_playback(pDevice, (ma_uint32)frameCount, pFrames, &pDevice->webaudio.duplexRB);
24901  } else {
24902  ma_device__read_frames_from_client(pDevice, (ma_uint32)frameCount, pFrames); /* Read directly from the device. */
24903  }
24904 }
24905 #ifdef __cplusplus
24906 }
24907 #endif
24908 
24909 ma_bool32 ma_context_is_device_id_equal__webaudio(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
24910 {
24911  ma_assert(pContext != NULL);
24912  ma_assert(pID0 != NULL);
24913  ma_assert(pID1 != NULL);
24914  (void)pContext;
24915 
24916  return ma_strcmp(pID0->webaudio, pID1->webaudio) == 0;
24917 }
24918 
24919 ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
24920 {
24921  ma_bool32 cbResult = MA_TRUE;
24922 
24923  ma_assert(pContext != NULL);
24924  ma_assert(callback != NULL);
24925 
24926  /* Only supporting default devices for now. */
24927 
24928  /* Playback. */
24929  if (cbResult) {
24930  ma_device_info deviceInfo;
24931  ma_zero_object(&deviceInfo);
24932  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
24933  cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
24934  }
24935 
24936  /* Capture. */
24937  if (cbResult) {
24938  if (ma_is_capture_supported__webaudio()) {
24939  ma_device_info deviceInfo;
24940  ma_zero_object(&deviceInfo);
24941  ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
24942  cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
24943  }
24944  }
24945 
24946  return MA_SUCCESS;
24947 }
24948 
24949 ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
24950 {
24951  ma_assert(pContext != NULL);
24952 
24953  /* No exclusive mode with Web Audio. */
24954  if (shareMode == ma_share_mode_exclusive) {
24956  }
24957 
24958  if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
24959  return MA_NO_DEVICE;
24960  }
24961 
24962 
24963  ma_zero_memory(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio));
24964 
24965  /* Only supporting default devices for now. */
24966  if (deviceType == ma_device_type_playback) {
24967  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
24968  } else {
24969  ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
24970  }
24971 
24972  /* Web Audio can support any number of channels and sample rates. It only supports f32 formats, however. */
24973  pDeviceInfo->minChannels = 1;
24974  pDeviceInfo->maxChannels = MA_MAX_CHANNELS;
24975  if (pDeviceInfo->maxChannels > 32) {
24976  pDeviceInfo->maxChannels = 32; /* Maximum output channel count is 32 for createScriptProcessor() (JavaScript). */
24977  }
24978 
24979  /* We can query the sample rate by just using a temporary audio context. */
24980  pDeviceInfo->minSampleRate = EM_ASM_INT({
24981  try {
24982  var temp = new (window.AudioContext || window.webkitAudioContext)();
24983  var sampleRate = temp.sampleRate;
24984  temp.close();
24985  return sampleRate;
24986  } catch(e) {
24987  return 0;
24988  }
24989  }, 0); /* Must pass in a dummy argument for C99 compatibility. */
24990  pDeviceInfo->maxSampleRate = pDeviceInfo->minSampleRate;
24991  if (pDeviceInfo->minSampleRate == 0) {
24992  return MA_NO_DEVICE;
24993  }
24994 
24995  /* Web Audio only supports f32. */
24996  pDeviceInfo->formatCount = 1;
24997  pDeviceInfo->formats[0] = ma_format_f32;
24998 
24999  return MA_SUCCESS;
25000 }
25001 
25002 
25003 void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_type deviceType, int deviceIndex)
25004 {
25005  ma_assert(pDevice != NULL);
25006 
25007  EM_ASM({
25008  var device = miniaudio.get_device_by_index($0);
25009 
25010  /* Make sure all nodes are disconnected and marked for collection. */
25011  if (device.scriptNode !== undefined) {
25012  device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */
25013  device.scriptNode.disconnect();
25014  device.scriptNode = undefined;
25015  }
25016  if (device.streamNode !== undefined) {
25017  device.streamNode.disconnect();
25018  device.streamNode = undefined;
25019  }
25020 
25021  /*
25022  Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want
25023  to clear the callback before closing.
25024  */
25025  device.webaudio.close();
25026  device.webaudio = undefined;
25027 
25028  /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */
25029  if (device.intermediaryBuffer !== undefined) {
25030  Module._free(device.intermediaryBuffer);
25031  device.intermediaryBuffer = undefined;
25032  device.intermediaryBufferView = undefined;
25033  device.intermediaryBufferSizeInBytes = undefined;
25034  }
25035 
25036  /* Make sure the device is untracked so the slot can be reused later. */
25037  miniaudio.untrack_device_by_index($0);
25038  }, deviceIndex, deviceType);
25039 }
25040 
25041 void ma_device_uninit__webaudio(ma_device* pDevice)
25042 {
25043  ma_assert(pDevice != NULL);
25044 
25045  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25046  ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
25047  }
25048 
25049  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25050  ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback);
25051  }
25052 
25053  if (pDevice->type == ma_device_type_duplex) {
25054  ma_pcm_rb_uninit(&pDevice->webaudio.duplexRB);
25055  }
25056 }
25057 
25058 ma_result ma_device_init_by_type__webaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device_type deviceType, ma_device* pDevice)
25059 {
25060  int deviceIndex;
25061  ma_uint32 internalBufferSizeInFrames;
25062 
25063  ma_assert(pContext != NULL);
25064  ma_assert(pConfig != NULL);
25065  ma_assert(deviceType != ma_device_type_duplex);
25066  ma_assert(pDevice != NULL);
25067 
25068  if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
25069  return MA_NO_DEVICE;
25070  }
25071 
25072  /* Try calculating an appropriate buffer size. */
25073  internalBufferSizeInFrames = pConfig->bufferSizeInFrames;
25074  if (internalBufferSizeInFrames == 0) {
25075  internalBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->bufferSizeInMilliseconds, pConfig->sampleRate);
25076  }
25077 
25078  /* The size of the buffer must be a power of 2 and between 256 and 16384. */
25079  if (internalBufferSizeInFrames < 256) {
25080  internalBufferSizeInFrames = 256;
25081  } else if (internalBufferSizeInFrames > 16384) {
25082  internalBufferSizeInFrames = 16384;
25083  } else {
25084  internalBufferSizeInFrames = ma_next_power_of_2(internalBufferSizeInFrames);
25085  }
25086 
25087  /* We create the device on the JavaScript side and reference it using an index. We use this to make it possible to reference the device between JavaScript and C. */
25088  deviceIndex = EM_ASM_INT({
25089  var channels = $0;
25090  var sampleRate = $1;
25091  var bufferSize = $2; /* In PCM frames. */
25092  var isCapture = $3;
25093  var pDevice = $4;
25094 
25095  if (typeof(miniaudio) === 'undefined') {
25096  return -1; /* Context not initialized. */
25097  }
25098 
25099  var device = {};
25100 
25101  /* The AudioContext must be created in a suspended state. */
25102  device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate});
25103  device.webaudio.suspend();
25104 
25105  /*
25106  We need an intermediary buffer which we use for JavaScript and C interop. This buffer stores interleaved f32 PCM data. Because it's passed between
25107  JavaScript and C it needs to be allocated and freed using Module._malloc() and Module._free().
25108  */
25109  device.intermediaryBufferSizeInBytes = channels * bufferSize * 4;
25110  device.intermediaryBuffer = Module._malloc(device.intermediaryBufferSizeInBytes);
25111  device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes);
25112 
25113  /*
25114  Both playback and capture devices use a ScriptProcessorNode for performing per-sample operations.
25115 
25116  ScriptProcessorNode is actually deprecated so this is likely to be temporary. The way this works for playback is very simple. You just set a callback
25117  that's periodically fired, just like a normal audio callback function. But apparently this design is "flawed" and is now deprecated in favour of
25118  something called AudioWorklets which _forces_ you to load a _separate_ .js file at run time... nice... Hopefully ScriptProcessorNode will continue to
25119  work for years to come, but this may need to change to use AudioSourceBufferNode instead, which I think is what Emscripten uses for it's built-in SDL
25120  implementation. I'll be avoiding that insane AudioWorklet API like the plague...
25121 
25122  For capture it is a bit unintuitive. We use the ScriptProccessorNode _only_ to get the raw PCM data. It is connected to an AudioContext just like the
25123  playback case, however we just output silence to the AudioContext instead of passing any real data. It would make more sense to me to use the
25124  MediaRecorder API, but unfortunately you need to specify a MIME time (Opus, Vorbis, etc.) for the binary blob that's returned to the client, but I've
25125  been unable to figure out how to get this as raw PCM. The closes I can think is to use the MIME type for WAV files and just parse it, but I don't know
25126  how well this would work. Although ScriptProccessorNode is deprecated, in practice it seems to have pretty good browser support so I'm leaving it like
25127  this for now. If anything knows how I could get raw PCM data using the MediaRecorder API please let me know!
25128  */
25129  device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channels, channels);
25130 
25131  if (isCapture) {
25132  device.scriptNode.onaudioprocess = function(e) {
25133  if (device.intermediaryBuffer === undefined) {
25134  return; /* This means the device has been uninitialized. */
25135  }
25136 
25137  /* Make sure silence it output to the AudioContext destination. Not doing this will cause sound to come out of the speakers! */
25138  for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
25139  e.outputBuffer.getChannelData(iChannel).fill(0.0);
25140  }
25141 
25142  /* There are some situations where we may want to send silence to the client. */
25143  var sendSilence = false;
25144  if (device.streamNode === undefined) {
25145  sendSilence = true;
25146  }
25147 
25148  /* Sanity check. This will never happen, right? */
25149  if (e.inputBuffer.numberOfChannels != channels) {
25150  console.log("Capture: Channel count mismatch. " + e.inputBufer.numberOfChannels + " != " + channels + ". Sending silence.");
25151  sendSilence = true;
25152  }
25153 
25154  /* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */
25155  var totalFramesProcessed = 0;
25156  while (totalFramesProcessed < e.inputBuffer.length) {
25157  var framesRemaining = e.inputBuffer.length - totalFramesProcessed;
25158  var framesToProcess = framesRemaining;
25159  if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
25160  framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
25161  }
25162 
25163  /* We need to do the reverse of the playback case. We need to interleave the input data and copy it into the intermediary buffer. Then we send it to the client. */
25164  if (sendSilence) {
25165  device.intermediaryBufferView.fill(0.0);
25166  } else {
25167  for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
25168  for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) {
25169  device.intermediaryBufferView[iFrame*channels + iChannel] = e.inputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame];
25170  }
25171  }
25172  }
25173 
25174  /* Send data to the client from our intermediary buffer. */
25175  ccall("ma_device_process_pcm_frames_capture__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
25176 
25177  totalFramesProcessed += framesToProcess;
25178  }
25179  };
25180 
25181  navigator.mediaDevices.getUserMedia({audio:true, video:false})
25182  .then(function(stream) {
25183  device.streamNode = device.webaudio.createMediaStreamSource(stream);
25184  device.streamNode.connect(device.scriptNode);
25185  device.scriptNode.connect(device.webaudio.destination);
25186  })
25187  .catch(function(error) {
25188  /* I think this should output silence... */
25189  device.scriptNode.connect(device.webaudio.destination);
25190  });
25191  } else {
25192  device.scriptNode.onaudioprocess = function(e) {
25193  if (device.intermediaryBuffer === undefined) {
25194  return; /* This means the device has been uninitialized. */
25195  }
25196 
25197  var outputSilence = false;
25198 
25199  /* Sanity check. This will never happen, right? */
25200  if (e.outputBuffer.numberOfChannels != channels) {
25201  console.log("Playback: Channel count mismatch. " + e.outputBufer.numberOfChannels + " != " + channels + ". Outputting silence.");
25202  outputSilence = true;
25203  return;
25204  }
25205 
25206  /* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */
25207  var totalFramesProcessed = 0;
25208  while (totalFramesProcessed < e.outputBuffer.length) {
25209  var framesRemaining = e.outputBuffer.length - totalFramesProcessed;
25210  var framesToProcess = framesRemaining;
25211  if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) {
25212  framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4);
25213  }
25214 
25215  /* Read data from the client into our intermediary buffer. */
25216  ccall("ma_device_process_pcm_frames_playback__webaudio", "undefined", ["number", "number", "number"], [pDevice, framesToProcess, device.intermediaryBuffer]);
25217 
25218  /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */
25219  if (outputSilence) {
25220  for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
25221  e.outputBuffer.getChannelData(iChannel).fill(0.0);
25222  }
25223  } else {
25224  for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
25225  for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) {
25226  e.outputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame] = device.intermediaryBufferView[iFrame*channels + iChannel];
25227  }
25228  }
25229  }
25230 
25231  totalFramesProcessed += framesToProcess;
25232  }
25233  };
25234 
25235  device.scriptNode.connect(device.webaudio.destination);
25236  }
25237 
25238  return miniaudio.track_device(device);
25239  }, (deviceType == ma_device_type_capture) ? pConfig->capture.channels : pConfig->playback.channels, pConfig->sampleRate, internalBufferSizeInFrames, deviceType == ma_device_type_capture, pDevice);
25240 
25241  if (deviceIndex < 0) {
25243  }
25244 
25245  if (deviceType == ma_device_type_capture) {
25246  pDevice->webaudio.indexCapture = deviceIndex;
25247  pDevice->capture.internalFormat = ma_format_f32;
25248  pDevice->capture.internalChannels = pConfig->capture.channels;
25249  ma_get_standard_channel_map(ma_standard_channel_map_webaudio, pDevice->capture.internalChannels, pDevice->capture.internalChannelMap);
25250  pDevice->capture.internalSampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
25251  pDevice->capture.internalBufferSizeInFrames = internalBufferSizeInFrames;
25252  pDevice->capture.internalPeriods = 1;
25253  } else {
25254  pDevice->webaudio.indexPlayback = deviceIndex;
25255  pDevice->playback.internalFormat = ma_format_f32;
25256  pDevice->playback.internalChannels = pConfig->playback.channels;
25257  ma_get_standard_channel_map(ma_standard_channel_map_webaudio, pDevice->playback.internalChannels, pDevice->playback.internalChannelMap);
25258  pDevice->playback.internalSampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
25259  pDevice->playback.internalBufferSizeInFrames = internalBufferSizeInFrames;
25260  pDevice->playback.internalPeriods = 1;
25261  }
25262 
25263  return MA_SUCCESS;
25264 }
25265 
25266 ma_result ma_device_init__webaudio(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
25267 {
25268  ma_result result;
25269 
25270  if (pConfig->deviceType == ma_device_type_loopback) {
25272  }
25273 
25274  /* No exclusive mode with Web Audio. */
25278  }
25279 
25280  if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
25281  result = ma_device_init_by_type__webaudio(pContext, pConfig, ma_device_type_capture, pDevice);
25282  if (result != MA_SUCCESS) {
25283  return result;
25284  }
25285  }
25286 
25287  if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
25288  result = ma_device_init_by_type__webaudio(pContext, pConfig, ma_device_type_playback, pDevice);
25289  if (result != MA_SUCCESS) {
25290  if (pConfig->deviceType == ma_device_type_duplex) {
25291  ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
25292  }
25293  return result;
25294  }
25295  }
25296 
25297  /*
25298  We need a ring buffer for moving data from the capture device to the playback device. The capture callback is the producer
25299  and the playback callback is the consumer. The buffer needs to be large enough to hold internalBufferSizeInFrames based on
25300  the external sample rate.
25301  */
25302  if (pConfig->deviceType == ma_device_type_duplex) {
25303  ma_uint32 rbSizeInFrames = (ma_uint32)ma_calculate_frame_count_after_src(pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalBufferSizeInFrames) * 2;
25304  result = ma_pcm_rb_init(pDevice->capture.format, pDevice->capture.channels, rbSizeInFrames, NULL, &pDevice->webaudio.duplexRB);
25305  if (result != MA_SUCCESS) {
25306  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25307  ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture);
25308  }
25309  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25310  ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback);
25311  }
25312  return result;
25313  }
25314 
25315  /* We need a period to act as a buffer for cases where the playback and capture device's end up desyncing. */
25316  {
25317  ma_uint32 marginSizeInFrames = rbSizeInFrames / 3; /* <-- Dividing by 3 because internalPeriods is always set to 1 for WebAudio. */
25318  void* pMarginData;
25319  ma_pcm_rb_acquire_write(&pDevice->webaudio.duplexRB, &marginSizeInFrames, &pMarginData);
25320  {
25321  ma_zero_memory(pMarginData, marginSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
25322  }
25323  ma_pcm_rb_commit_write(&pDevice->webaudio.duplexRB, marginSizeInFrames, pMarginData);
25324  }
25325  }
25326 
25327  return MA_SUCCESS;
25328 }
25329 
25330 ma_result ma_device_start__webaudio(ma_device* pDevice)
25331 {
25332  ma_assert(pDevice != NULL);
25333 
25334  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25335  EM_ASM({
25336  miniaudio.get_device_by_index($0).webaudio.resume();
25337  }, pDevice->webaudio.indexCapture);
25338  }
25339 
25340  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25341  EM_ASM({
25342  miniaudio.get_device_by_index($0).webaudio.resume();
25343  }, pDevice->webaudio.indexPlayback);
25344  }
25345 
25346  return MA_SUCCESS;
25347 }
25348 
25349 ma_result ma_device_stop__webaudio(ma_device* pDevice)
25350 {
25351  ma_assert(pDevice != NULL);
25352 
25353  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
25354  EM_ASM({
25355  miniaudio.get_device_by_index($0).webaudio.suspend();
25356  }, pDevice->webaudio.indexCapture);
25357  }
25358 
25359  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
25360  EM_ASM({
25361  miniaudio.get_device_by_index($0).webaudio.suspend();
25362  }, pDevice->webaudio.indexPlayback);
25363  }
25364 
25365  ma_stop_proc onStop = pDevice->onStop;
25366  if (onStop) {
25367  onStop(pDevice);
25368  }
25369 
25370  return MA_SUCCESS;
25371 }
25372 
25373 ma_result ma_context_uninit__webaudio(ma_context* pContext)
25374 {
25375  ma_assert(pContext != NULL);
25376  ma_assert(pContext->backend == ma_backend_webaudio);
25377 
25378  /* Nothing needs to be done here. */
25379  (void)pContext;
25380 
25381  return MA_SUCCESS;
25382 }
25383 
25384 ma_result ma_context_init__webaudio(const ma_context_config* pConfig, ma_context* pContext)
25385 {
25386  int resultFromJS;
25387 
25388  ma_assert(pContext != NULL);
25389 
25390  /* Here is where our global JavaScript object is initialized. */
25391  resultFromJS = EM_ASM_INT({
25392  if ((window.AudioContext || window.webkitAudioContext) === undefined) {
25393  return 0; /* Web Audio not supported. */
25394  }
25395 
25396  if (typeof(miniaudio) === 'undefined') {
25397  miniaudio = {};
25398  miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */
25399 
25400  miniaudio.track_device = function(device) {
25401  /* Try inserting into a free slot first. */
25402  for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
25403  if (miniaudio.devices[iDevice] == null) {
25404  miniaudio.devices[iDevice] = device;
25405  return iDevice;
25406  }
25407  }
25408 
25409  /* Getting here means there is no empty slots in the array so we just push to the end. */
25410  miniaudio.devices.push(device);
25411  return miniaudio.devices.length - 1;
25412  };
25413 
25414  miniaudio.untrack_device_by_index = function(deviceIndex) {
25415  /* We just set the device's slot to null. The slot will get reused in the next call to ma_track_device. */
25416  miniaudio.devices[deviceIndex] = null;
25417 
25418  /* Trim the array if possible. */
25419  while (miniaudio.devices.length > 0) {
25420  if (miniaudio.devices[miniaudio.devices.length-1] == null) {
25421  miniaudio.devices.pop();
25422  } else {
25423  break;
25424  }
25425  }
25426  };
25427 
25428  miniaudio.untrack_device = function(device) {
25429  for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
25430  if (miniaudio.devices[iDevice] == device) {
25431  return miniaudio.untrack_device_by_index(iDevice);
25432  }
25433  }
25434  };
25435 
25436  miniaudio.get_device_by_index = function(deviceIndex) {
25437  return miniaudio.devices[deviceIndex];
25438  };
25439  }
25440 
25441  return 1;
25442  }, 0); /* Must pass in a dummy argument for C99 compatibility. */
25443 
25444  if (resultFromJS != 1) {
25446  }
25447 
25448 
25449  pContext->isBackendAsynchronous = MA_TRUE;
25450 
25451  pContext->onUninit = ma_context_uninit__webaudio;
25452  pContext->onDeviceIDEqual = ma_context_is_device_id_equal__webaudio;
25453  pContext->onEnumDevices = ma_context_enumerate_devices__webaudio;
25454  pContext->onGetDeviceInfo = ma_context_get_device_info__webaudio;
25455  pContext->onDeviceInit = ma_device_init__webaudio;
25456  pContext->onDeviceUninit = ma_device_uninit__webaudio;
25457  pContext->onDeviceStart = ma_device_start__webaudio;
25458  pContext->onDeviceStop = ma_device_stop__webaudio;
25459 
25460  (void)pConfig; /* Unused. */
25461  return MA_SUCCESS;
25462 }
25463 #endif /* Web Audio */
25464 
25465 
25466 
25467 ma_bool32 ma__is_channel_map_valid(const ma_channel* channelMap, ma_uint32 channels)
25468 {
25469  /* A blank channel map should be allowed, in which case it should use an appropriate default which will depend on context. */
25470  if (channelMap[0] != MA_CHANNEL_NONE) {
25471  ma_uint32 iChannel;
25472 
25473  if (channels == 0) {
25474  return MA_FALSE; /* No channels. */
25475  }
25476 
25477  /* A channel cannot be present in the channel map more than once. */
25478  for (iChannel = 0; iChannel < channels; ++iChannel) {
25479  ma_uint32 jChannel;
25480  for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) {
25481  if (channelMap[iChannel] == channelMap[jChannel]) {
25482  return MA_FALSE;
25483  }
25484  }
25485  }
25486  }
25487 
25488  return MA_TRUE;
25489 }
25490 
25491 
25492 void ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType)
25493 {
25494  ma_assert(pDevice != NULL);
25495 
25496  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
25497  if (pDevice->capture.usingDefaultFormat) {
25498  pDevice->capture.format = pDevice->capture.internalFormat;
25499  }
25500  if (pDevice->capture.usingDefaultChannels) {
25501  pDevice->capture.channels = pDevice->capture.internalChannels;
25502  }
25503  if (pDevice->capture.usingDefaultChannelMap) {
25504  if (pDevice->capture.internalChannels == pDevice->capture.channels) {
25505  ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels);
25506  } else {
25507  ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->capture.channels, pDevice->capture.channelMap);
25508  }
25509  }
25510  }
25511 
25512  if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
25513  if (pDevice->playback.usingDefaultFormat) {
25514  pDevice->playback.format = pDevice->playback.internalFormat;
25515  }
25516  if (pDevice->playback.usingDefaultChannels) {
25517  pDevice->playback.channels = pDevice->playback.internalChannels;
25518  }
25519  if (pDevice->playback.usingDefaultChannelMap) {
25520  if (pDevice->playback.internalChannels == pDevice->playback.channels) {
25521  ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels);
25522  } else {
25523  ma_get_standard_channel_map(ma_standard_channel_map_default, pDevice->playback.channels, pDevice->playback.channelMap);
25524  }
25525  }
25526  }
25527 
25528  if (pDevice->usingDefaultSampleRate) {
25529  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex) {
25530  pDevice->sampleRate = pDevice->capture.internalSampleRate;
25531  } else {
25532  pDevice->sampleRate = pDevice->playback.internalSampleRate;
25533  }
25534  }
25535 
25536  /* PCM converters. */
25537  if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
25538  /* Converting from internal device format to public format. */
25540  converterConfig.neverConsumeEndOfInput = MA_TRUE;
25541  converterConfig.pUserData = pDevice;
25542  converterConfig.formatIn = pDevice->capture.internalFormat;
25543  converterConfig.channelsIn = pDevice->capture.internalChannels;
25544  converterConfig.sampleRateIn = pDevice->capture.internalSampleRate;
25545  ma_channel_map_copy(converterConfig.channelMapIn, pDevice->capture.internalChannelMap, pDevice->capture.internalChannels);
25546  converterConfig.formatOut = pDevice->capture.format;
25547  converterConfig.channelsOut = pDevice->capture.channels;
25548  converterConfig.sampleRateOut = pDevice->sampleRate;
25549  ma_channel_map_copy(converterConfig.channelMapOut, pDevice->capture.channelMap, pDevice->capture.channels);
25550  converterConfig.onRead = ma_device__pcm_converter__on_read_from_buffer_capture;
25551  ma_pcm_converter_init(&converterConfig, &pDevice->capture.converter);
25552  }
25553 
25554  if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
25555  /* Converting from public format to device format. */
25557  converterConfig.neverConsumeEndOfInput = MA_TRUE;
25558  converterConfig.pUserData = pDevice;
25559  converterConfig.formatIn = pDevice->playback.format;
25560  converterConfig.channelsIn = pDevice->playback.channels;
25561  converterConfig.sampleRateIn = pDevice->sampleRate;
25562  ma_channel_map_copy(converterConfig.channelMapIn, pDevice->playback.channelMap, pDevice->playback.channels);
25563  converterConfig.formatOut = pDevice->playback.internalFormat;
25564  converterConfig.channelsOut = pDevice->playback.internalChannels;
25565  converterConfig.sampleRateOut = pDevice->playback.internalSampleRate;
25566  ma_channel_map_copy(converterConfig.channelMapOut, pDevice->playback.internalChannelMap, pDevice->playback.internalChannels);
25567  if (deviceType == ma_device_type_playback) {
25568  if (pDevice->type == ma_device_type_playback) {
25569  converterConfig.onRead = ma_device__on_read_from_client;
25570  } else {
25571  converterConfig.onRead = ma_device__pcm_converter__on_read_from_buffer_playback;
25572  }
25573  } else {
25574  converterConfig.onRead = ma_device__pcm_converter__on_read_from_buffer_playback;
25575  }
25576  ma_pcm_converter_init(&converterConfig, &pDevice->playback.converter);
25577  }
25578 }
25579 
25580 
25581 ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
25582 {
25583  ma_device* pDevice = (ma_device*)pData;
25584  ma_assert(pDevice != NULL);
25585 
25586 #ifdef MA_WIN32
25587  ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE);
25588 #endif
25589 
25590  /*
25591  When the device is being initialized it's initial state is set to MA_STATE_UNINITIALIZED. Before returning from
25592  ma_device_init(), the state needs to be set to something valid. In miniaudio the device's default state immediately
25593  after initialization is stopped, so therefore we need to mark the device as such. miniaudio will wait on the worker
25594  thread to signal an event to know when the worker thread is ready for action.
25595  */
25596  ma_device__set_state(pDevice, MA_STATE_STOPPED);
25597  ma_event_signal(&pDevice->stopEvent);
25598 
25599  for (;;) { /* <-- This loop just keeps the thread alive. The main audio loop is inside. */
25600  ma_stop_proc onStop;
25601 
25602  /* We wait on an event to know when something has requested that the device be started and the main loop entered. */
25603  ma_event_wait(&pDevice->wakeupEvent);
25604 
25605  /* Default result code. */
25606  pDevice->workResult = MA_SUCCESS;
25607 
25608  /* If the reason for the wake up is that we are terminating, just break from the loop. */
25609  if (ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED) {
25610  break;
25611  }
25612 
25613  /*
25614  Getting to this point means the device is wanting to get started. The function that has requested that the device
25615  be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event
25616  in both the success and error case. It's important that the state of the device is set _before_ signaling the event.
25617  */
25618  ma_assert(ma_device__get_state(pDevice) == MA_STATE_STARTING);
25619 
25620  /* Make sure the state is set appropriately. */
25621  ma_device__set_state(pDevice, MA_STATE_STARTED);
25622  ma_event_signal(&pDevice->startEvent);
25623 
25624  if (pDevice->pContext->onDeviceMainLoop != NULL) {
25625  pDevice->pContext->onDeviceMainLoop(pDevice);
25626  } else {
25627  ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "No main loop implementation.", MA_API_NOT_FOUND);
25628  }
25629 
25630  /*
25631  Getting here means we have broken from the main loop which happens the application has requested that device be stopped. Note that this
25632  may have actually already happened above if the device was lost and miniaudio has attempted to re-initialize the device. In this case we
25633  don't want to be doing this a second time.
25634  */
25635  if (ma_device__get_state(pDevice) != MA_STATE_UNINITIALIZED) {
25636  if (pDevice->pContext->onDeviceStop) {
25637  pDevice->pContext->onDeviceStop(pDevice);
25638  }
25639  }
25640 
25641  /* After the device has stopped, make sure an event is posted. */
25642  onStop = pDevice->onStop;
25643  if (onStop) {
25644  onStop(pDevice);
25645  }
25646 
25647  /*
25648  A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. Note that
25649  it's possible that the device has been uninitialized which means we need to _not_ change the status to stopped. We cannot go from an
25650  uninitialized state to stopped state.
25651  */
25652  if (ma_device__get_state(pDevice) != MA_STATE_UNINITIALIZED) {
25653  ma_device__set_state(pDevice, MA_STATE_STOPPED);
25654  ma_event_signal(&pDevice->stopEvent);
25655  }
25656  }
25657 
25658  /* Make sure we aren't continuously waiting on a stop event. */
25659  ma_event_signal(&pDevice->stopEvent); /* <-- Is this still needed? */
25660 
25661 #ifdef MA_WIN32
25662  ma_CoUninitialize(pDevice->pContext);
25663 #endif
25664 
25665  return (ma_thread_result)0;
25666 }
25667 
25668 
25669 /* Helper for determining whether or not the given device is initialized. */
25670 ma_bool32 ma_device__is_initialized(ma_device* pDevice)
25671 {
25672  if (pDevice == NULL) {
25673  return MA_FALSE;
25674  }
25675 
25676  return ma_device__get_state(pDevice) != MA_STATE_UNINITIALIZED;
25677 }
25678 
25679 
25680 #ifdef MA_WIN32
25681 ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext)
25682 {
25683  ma_CoUninitialize(pContext);
25684  ma_dlclose(pContext, pContext->win32.hUser32DLL);
25685  ma_dlclose(pContext, pContext->win32.hOle32DLL);
25686  ma_dlclose(pContext, pContext->win32.hAdvapi32DLL);
25687 
25688  return MA_SUCCESS;
25689 }
25690 
25691 ma_result ma_context_init_backend_apis__win32(ma_context* pContext)
25692 {
25693 #ifdef MA_WIN32_DESKTOP
25694  /* Ole32.dll */
25695  pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll");
25696  if (pContext->win32.hOle32DLL == NULL) {
25698  }
25699 
25700  pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx");
25701  pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize");
25702  pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance");
25703  pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree");
25704  pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear");
25705  pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2");
25706 
25707 
25708  /* User32.dll */
25709  pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll");
25710  if (pContext->win32.hUser32DLL == NULL) {
25712  }
25713 
25714  pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow");
25715  pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow");
25716 
25717 
25718  /* Advapi32.dll */
25719  pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll");
25720  if (pContext->win32.hAdvapi32DLL == NULL) {
25722  }
25723 
25724  pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA");
25725  pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey");
25726  pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA");
25727 #endif
25728 
25729  ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
25730  return MA_SUCCESS;
25731 }
25732 #else
25733 ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext)
25734 {
25735 #if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
25736  ma_dlclose(pContext, pContext->posix.pthreadSO);
25737 #else
25738  (void)pContext;
25739 #endif
25740 
25741  return MA_SUCCESS;
25742 }
25743 
25744 ma_result ma_context_init_backend_apis__nix(ma_context* pContext)
25745 {
25746  /* pthread */
25747 #if defined(MA_USE_RUNTIME_LINKING_FOR_PTHREAD) && !defined(MA_NO_RUNTIME_LINKING)
25748  const char* libpthreadFileNames[] = {
25749  "libpthread.so",
25750  "libpthread.so.0",
25751  "libpthread.dylib"
25752  };
25753  size_t i;
25754 
25755  for (i = 0; i < sizeof(libpthreadFileNames) / sizeof(libpthreadFileNames[0]); ++i) {
25756  pContext->posix.pthreadSO = ma_dlopen(pContext, libpthreadFileNames[i]);
25757  if (pContext->posix.pthreadSO != NULL) {
25758  break;
25759  }
25760  }
25761 
25762  if (pContext->posix.pthreadSO == NULL) {
25764  }
25765 
25766  pContext->posix.pthread_create = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_create");
25767  pContext->posix.pthread_join = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_join");
25768  pContext->posix.pthread_mutex_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_init");
25769  pContext->posix.pthread_mutex_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_destroy");
25770  pContext->posix.pthread_mutex_lock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_lock");
25771  pContext->posix.pthread_mutex_unlock = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_mutex_unlock");
25772  pContext->posix.pthread_cond_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_init");
25773  pContext->posix.pthread_cond_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_destroy");
25774  pContext->posix.pthread_cond_wait = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_wait");
25775  pContext->posix.pthread_cond_signal = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_cond_signal");
25776  pContext->posix.pthread_attr_init = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_init");
25777  pContext->posix.pthread_attr_destroy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_destroy");
25778  pContext->posix.pthread_attr_setschedpolicy = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedpolicy");
25779  pContext->posix.pthread_attr_getschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_getschedparam");
25780  pContext->posix.pthread_attr_setschedparam = (ma_proc)ma_dlsym(pContext, pContext->posix.pthreadSO, "pthread_attr_setschedparam");
25781 #else
25782  pContext->posix.pthread_create = (ma_proc)pthread_create;
25783  pContext->posix.pthread_join = (ma_proc)pthread_join;
25784  pContext->posix.pthread_mutex_init = (ma_proc)pthread_mutex_init;
25785  pContext->posix.pthread_mutex_destroy = (ma_proc)pthread_mutex_destroy;
25786  pContext->posix.pthread_mutex_lock = (ma_proc)pthread_mutex_lock;
25787  pContext->posix.pthread_mutex_unlock = (ma_proc)pthread_mutex_unlock;
25788  pContext->posix.pthread_cond_init = (ma_proc)pthread_cond_init;
25789  pContext->posix.pthread_cond_destroy = (ma_proc)pthread_cond_destroy;
25790  pContext->posix.pthread_cond_wait = (ma_proc)pthread_cond_wait;
25791  pContext->posix.pthread_cond_signal = (ma_proc)pthread_cond_signal;
25792  pContext->posix.pthread_attr_init = (ma_proc)pthread_attr_init;
25793  pContext->posix.pthread_attr_destroy = (ma_proc)pthread_attr_destroy;
25794 #if !defined(__EMSCRIPTEN__)
25795  pContext->posix.pthread_attr_setschedpolicy = (ma_proc)pthread_attr_setschedpolicy;
25796  pContext->posix.pthread_attr_getschedparam = (ma_proc)pthread_attr_getschedparam;
25797  pContext->posix.pthread_attr_setschedparam = (ma_proc)pthread_attr_setschedparam;
25798 #endif
25799 #endif
25800 
25801  return MA_SUCCESS;
25802 }
25803 #endif
25804 
25805 ma_result ma_context_init_backend_apis(ma_context* pContext)
25806 {
25807  ma_result result;
25808 #ifdef MA_WIN32
25809  result = ma_context_init_backend_apis__win32(pContext);
25810 #else
25811  result = ma_context_init_backend_apis__nix(pContext);
25812 #endif
25813 
25814  return result;
25815 }
25816 
25817 ma_result ma_context_uninit_backend_apis(ma_context* pContext)
25818 {
25819  ma_result result;
25820 #ifdef MA_WIN32
25821  result = ma_context_uninit_backend_apis__win32(pContext);
25822 #else
25823  result = ma_context_uninit_backend_apis__nix(pContext);
25824 #endif
25825 
25826  return result;
25827 }
25828 
25829 
25830 ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext)
25831 {
25832  return pContext->isBackendAsynchronous;
25833 }
25834 
25835 ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext)
25836 {
25837  ma_result result;
25839  ma_backend defaultBackends[ma_backend_null+1];
25840  ma_uint32 iBackend;
25841  ma_backend* pBackendsToIterate;
25842  ma_uint32 backendsToIterateCount;
25843 
25844  if (pContext == NULL) {
25845  return MA_INVALID_ARGS;
25846  }
25847 
25848  ma_zero_object(pContext);
25849 
25850  /* Always make sure the config is set first to ensure properties are available as soon as possible. */
25851  if (pConfig != NULL) {
25852  config = *pConfig;
25853  } else {
25855  }
25856 
25857  pContext->logCallback = config.logCallback;
25858  pContext->threadPriority = config.threadPriority;
25859  pContext->pUserData = config.pUserData;
25860 
25861  /* Backend APIs need to be initialized first. This is where external libraries will be loaded and linked. */
25862  result = ma_context_init_backend_apis(pContext);
25863  if (result != MA_SUCCESS) {
25864  return result;
25865  }
25866 
25867  for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
25868  defaultBackends[iBackend] = (ma_backend)iBackend;
25869  }
25870 
25871  pBackendsToIterate = (ma_backend*)backends;
25872  backendsToIterateCount = backendCount;
25873  if (pBackendsToIterate == NULL) {
25874  pBackendsToIterate = (ma_backend*)defaultBackends;
25875  backendsToIterateCount = ma_countof(defaultBackends);
25876  }
25877 
25878  ma_assert(pBackendsToIterate != NULL);
25879 
25880  for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
25881  ma_backend backend = pBackendsToIterate[iBackend];
25882 
25884  switch (backend) {
25885  #ifdef MA_HAS_WASAPI
25886  case ma_backend_wasapi:
25887  {
25888  result = ma_context_init__wasapi(&config, pContext);
25889  } break;
25890  #endif
25891  #ifdef MA_HAS_DSOUND
25892  case ma_backend_dsound:
25893  {
25894  result = ma_context_init__dsound(&config, pContext);
25895  } break;
25896  #endif
25897  #ifdef MA_HAS_WINMM
25898  case ma_backend_winmm:
25899  {
25900  result = ma_context_init__winmm(&config, pContext);
25901  } break;
25902  #endif
25903  #ifdef MA_HAS_ALSA
25904  case ma_backend_alsa:
25905  {
25906  result = ma_context_init__alsa(&config, pContext);
25907  } break;
25908  #endif
25909  #ifdef MA_HAS_PULSEAUDIO
25910  case ma_backend_pulseaudio:
25911  {
25912  result = ma_context_init__pulse(&config, pContext);
25913  } break;
25914  #endif
25915  #ifdef MA_HAS_JACK
25916  case ma_backend_jack:
25917  {
25918  result = ma_context_init__jack(&config, pContext);
25919  } break;
25920  #endif
25921  #ifdef MA_HAS_COREAUDIO
25922  case ma_backend_coreaudio:
25923  {
25924  result = ma_context_init__coreaudio(&config, pContext);
25925  } break;
25926  #endif
25927  #ifdef MA_HAS_SNDIO
25928  case ma_backend_sndio:
25929  {
25930  result = ma_context_init__sndio(&config, pContext);
25931  } break;
25932  #endif
25933  #ifdef MA_HAS_AUDIO4
25934  case ma_backend_audio4:
25935  {
25936  result = ma_context_init__audio4(&config, pContext);
25937  } break;
25938  #endif
25939  #ifdef MA_HAS_OSS
25940  case ma_backend_oss:
25941  {
25942  result = ma_context_init__oss(&config, pContext);
25943  } break;
25944  #endif
25945  #ifdef MA_HAS_AAUDIO
25946  case ma_backend_aaudio:
25947  {
25948  result = ma_context_init__aaudio(&config, pContext);
25949  } break;
25950  #endif
25951  #ifdef MA_HAS_OPENSL
25952  case ma_backend_opensl:
25953  {
25954  result = ma_context_init__opensl(&config, pContext);
25955  } break;
25956  #endif
25957  #ifdef MA_HAS_WEBAUDIO
25958  case ma_backend_webaudio:
25959  {
25960  result = ma_context_init__webaudio(&config, pContext);
25961  } break;
25962  #endif
25963  #ifdef MA_HAS_NULL
25964  case ma_backend_null:
25965  {
25966  result = ma_context_init__null(&config, pContext);
25967  } break;
25968  #endif
25969 
25970  default: break;
25971  }
25972 
25973  /* If this iteration was successful, return. */
25974  if (result == MA_SUCCESS) {
25975  result = ma_mutex_init(pContext, &pContext->deviceEnumLock);
25976  if (result != MA_SUCCESS) {
25977  ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.", MA_FAILED_TO_CREATE_MUTEX);
25978  }
25979  result = ma_mutex_init(pContext, &pContext->deviceInfoLock);
25980  if (result != MA_SUCCESS) {
25981  ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.", MA_FAILED_TO_CREATE_MUTEX);
25982  }
25983 
25984 #ifdef MA_DEBUG_OUTPUT
25985  printf("[miniaudio] Endian: %s\n", ma_is_little_endian() ? "LE" : "BE");
25986  printf("[miniaudio] SSE2: %s\n", ma_has_sse2() ? "YES" : "NO");
25987  printf("[miniaudio] AVX2: %s\n", ma_has_avx2() ? "YES" : "NO");
25988  printf("[miniaudio] AVX512F: %s\n", ma_has_avx512f() ? "YES" : "NO");
25989  printf("[miniaudio] NEON: %s\n", ma_has_neon() ? "YES" : "NO");
25990 #endif
25991 
25992  pContext->backend = backend;
25993  return result;
25994  }
25995  }
25996 
25997  /* If we get here it means an error occurred. */
25998  ma_zero_object(pContext); /* Safety. */
25999  return MA_NO_BACKEND;
26000 }
26001 
26003 {
26004  if (pContext == NULL) {
26005  return MA_INVALID_ARGS;
26006  }
26007 
26008  pContext->onUninit(pContext);
26009 
26010  ma_mutex_uninit(&pContext->deviceEnumLock);
26011  ma_mutex_uninit(&pContext->deviceInfoLock);
26012  ma_free(pContext->pDeviceInfos);
26013  ma_context_uninit_backend_apis(pContext);
26014 
26015  return MA_SUCCESS;
26016 }
26017 
26018 
26020 {
26021  ma_result result;
26022 
26023  if (pContext == NULL || pContext->onEnumDevices == NULL || callback == NULL) {
26024  return MA_INVALID_ARGS;
26025  }
26026 
26027  ma_mutex_lock(&pContext->deviceEnumLock);
26028  {
26029  result = pContext->onEnumDevices(pContext, callback, pUserData);
26030  }
26031  ma_mutex_unlock(&pContext->deviceEnumLock);
26032 
26033  return result;
26034 }
26035 
26036 
26037 ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
26038 {
26039  /*
26040  We need to insert the device info into our main internal buffer. Where it goes depends on the device type. If it's a capture device
26041  it's just appended to the end. If it's a playback device it's inserted just before the first capture device.
26042  */
26043 
26044  /*
26045  First make sure we have room. Since the number of devices we add to the list is usually relatively small I've decided to use a
26046  simple fixed size increment for buffer expansion.
26047  */
26048  const ma_uint32 bufferExpansionCount = 2;
26049  const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount;
26050 
26051  if (pContext->deviceInfoCapacity >= totalDeviceInfoCount) {
26052  ma_uint32 newCapacity = totalDeviceInfoCount + bufferExpansionCount;
26053  ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity);
26054  if (pNewInfos == NULL) {
26055  return MA_FALSE; /* Out of memory. */
26056  }
26057 
26058  pContext->pDeviceInfos = pNewInfos;
26059  pContext->deviceInfoCapacity = newCapacity;
26060  }
26061 
26062  if (deviceType == ma_device_type_playback) {
26063  /* Playback. Insert just before the first capture device. */
26064 
26065  /* The first thing to do is move all of the capture devices down a slot. */
26066  ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount;
26067  size_t iCaptureDevice;
26068  for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) {
26069  pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1];
26070  }
26071 
26072  /* Now just insert where the first capture device was before moving it down a slot. */
26073  pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo;
26074  pContext->playbackDeviceInfoCount += 1;
26075  } else {
26076  /* Capture. Insert at the end. */
26077  pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo;
26078  pContext->captureDeviceInfoCount += 1;
26079  }
26080 
26081  (void)pUserData;
26082  return MA_TRUE;
26083 }
26084 
26085 ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount)
26086 {
26087  ma_result result;
26088 
26089  /* Safety. */
26090  if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL;
26091  if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0;
26092  if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL;
26093  if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0;
26094 
26095  if (pContext == NULL || pContext->onEnumDevices == NULL) {
26096  return MA_INVALID_ARGS;
26097  }
26098 
26099  /* Note that we don't use ma_context_enumerate_devices() here because we want to do locking at a higher level. */
26100  ma_mutex_lock(&pContext->deviceEnumLock);
26101  {
26102  /* Reset everything first. */
26103  pContext->playbackDeviceInfoCount = 0;
26104  pContext->captureDeviceInfoCount = 0;
26105 
26106  /* Now enumerate over available devices. */
26107  result = pContext->onEnumDevices(pContext, ma_context_get_devices__enum_callback, NULL);
26108  if (result == MA_SUCCESS) {
26109  /* Playback devices. */
26110  if (ppPlaybackDeviceInfos != NULL) {
26111  *ppPlaybackDeviceInfos = pContext->pDeviceInfos;
26112  }
26113  if (pPlaybackDeviceCount != NULL) {
26114  *pPlaybackDeviceCount = pContext->playbackDeviceInfoCount;
26115  }
26116 
26117  /* Capture devices. */
26118  if (ppCaptureDeviceInfos != NULL) {
26119  *ppCaptureDeviceInfos = pContext->pDeviceInfos + pContext->playbackDeviceInfoCount; /* Capture devices come after playback devices. */
26120  }
26121  if (pCaptureDeviceCount != NULL) {
26122  *pCaptureDeviceCount = pContext->captureDeviceInfoCount;
26123  }
26124  }
26125  }
26126  ma_mutex_unlock(&pContext->deviceEnumLock);
26127 
26128  return result;
26129 }
26130 
26131 ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, ma_device_info* pDeviceInfo)
26132 {
26133  ma_device_info deviceInfo;
26134 
26135  /* NOTE: Do not clear pDeviceInfo on entry. The reason is the pDeviceID may actually point to pDeviceInfo->id which will break things. */
26136  if (pContext == NULL || pDeviceInfo == NULL) {
26137  return MA_INVALID_ARGS;
26138  }
26139 
26140  ma_zero_object(&deviceInfo);
26141 
26142  /* Help the backend out by copying over the device ID if we have one. */
26143  if (pDeviceID != NULL) {
26144  ma_copy_memory(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID));
26145  }
26146 
26147  /* The backend may have an optimized device info retrieval function. If so, try that first. */
26148  if (pContext->onGetDeviceInfo != NULL) {
26149  ma_result result;
26150  ma_mutex_lock(&pContext->deviceInfoLock);
26151  {
26152  result = pContext->onGetDeviceInfo(pContext, deviceType, pDeviceID, shareMode, &deviceInfo);
26153  }
26154  ma_mutex_unlock(&pContext->deviceInfoLock);
26155 
26156  /* Clamp ranges. */
26157  deviceInfo.minChannels = ma_max(deviceInfo.minChannels, MA_MIN_CHANNELS);
26158  deviceInfo.maxChannels = ma_min(deviceInfo.maxChannels, MA_MAX_CHANNELS);
26159  deviceInfo.minSampleRate = ma_max(deviceInfo.minSampleRate, MA_MIN_SAMPLE_RATE);
26160  deviceInfo.maxSampleRate = ma_min(deviceInfo.maxSampleRate, MA_MAX_SAMPLE_RATE);
26161 
26162  *pDeviceInfo = deviceInfo;
26163  return result;
26164  }
26165 
26166  /* Getting here means onGetDeviceInfo has not been set. */
26167  return MA_ERROR;
26168 }
26169 
26171 {
26172  if (pContext == NULL) {
26173  return MA_FALSE;
26174  }
26175 
26176  return ma_is_loopback_supported(pContext->backend);
26177 }
26178 
26179 
26180 ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
26181 {
26182  ma_result result;
26184 
26185  if (pContext == NULL) {
26186  return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice);
26187  }
26188  if (pDevice == NULL) {
26189  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
26190  }
26191  if (pConfig == NULL) {
26192  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid arguments (pConfig == NULL).", MA_INVALID_ARGS);
26193  }
26194 
26195  /* We need to make a copy of the config so we can set default values if they were left unset in the input config. */
26196  config = *pConfig;
26197 
26198  /* Basic config validation. */
26199  if (config.deviceType != ma_device_type_playback && config.deviceType != ma_device_type_capture && config.deviceType != ma_device_type_duplex && config.deviceType != ma_device_type_loopback) {
26200  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with an invalid config. Device type is invalid. Make sure the device type has been set in the config.", MA_INVALID_DEVICE_CONFIG);
26201  }
26202 
26203  if (config.deviceType == ma_device_type_capture || config.deviceType == ma_device_type_duplex) {
26204  if (config.capture.channels > MA_MAX_CHANNELS) {
26205  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with an invalid config. Capture channel count cannot exceed 32.", MA_INVALID_DEVICE_CONFIG);
26206  }
26207  if (!ma__is_channel_map_valid(config.capture.channelMap, config.capture.channels)) {
26208  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid config. Capture channel map is invalid.", MA_INVALID_DEVICE_CONFIG);
26209  }
26210  }
26211 
26212  if (config.deviceType == ma_device_type_playback || config.deviceType == ma_device_type_duplex || config.deviceType == ma_device_type_loopback) {
26213  if (config.playback.channels > MA_MAX_CHANNELS) {
26214  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with an invalid config. Playback channel count cannot exceed 32.", MA_INVALID_DEVICE_CONFIG);
26215  }
26216  if (!ma__is_channel_map_valid(config.playback.channelMap, config.playback.channels)) {
26217  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "ma_device_init() called with invalid config. Playback channel map is invalid.", MA_INVALID_DEVICE_CONFIG);
26218  }
26219  }
26220 
26221 
26222  ma_zero_object(pDevice);
26223  pDevice->pContext = pContext;
26224 
26225  /* Set the user data and log callback ASAP to ensure it is available for the entire initialization process. */
26226  pDevice->pUserData = config.pUserData;
26227  pDevice->onData = config.dataCallback;
26228  pDevice->onStop = config.stopCallback;
26229 
26230  if (((ma_uintptr)pDevice % sizeof(pDevice)) != 0) {
26231  if (pContext->logCallback) {
26232  pContext->logCallback(pContext, pDevice, MA_LOG_LEVEL_WARNING, "WARNING: ma_device_init() called for a device that is not properly aligned. Thread safety is not supported.");
26233  }
26234  }
26235 
26236  pDevice->noPreZeroedOutputBuffer = config.noPreZeroedOutputBuffer;
26237  pDevice->noClip = config.noClip;
26238  pDevice->masterVolumeFactor = 1;
26239 
26240  /*
26241  When passing in 0 for the format/channels/rate/chmap it means the device will be using whatever is chosen by the backend. If everything is set
26242  to defaults it means the format conversion pipeline will run on a fast path where data transfer is just passed straight through to the backend.
26243  */
26244  if (config.sampleRate == 0) {
26245  config.sampleRate = MA_DEFAULT_SAMPLE_RATE;
26246  pDevice->usingDefaultSampleRate = MA_TRUE;
26247  }
26248 
26249  if (config.capture.format == ma_format_unknown) {
26250  config.capture.format = MA_DEFAULT_FORMAT;
26251  pDevice->capture.usingDefaultFormat = MA_TRUE;
26252  }
26253  if (config.capture.channels == 0) {
26254  config.capture.channels = MA_DEFAULT_CHANNELS;
26255  pDevice->capture.usingDefaultChannels = MA_TRUE;
26256  }
26257  if (config.capture.channelMap[0] == MA_CHANNEL_NONE) {
26258  pDevice->capture.usingDefaultChannelMap = MA_TRUE;
26259  }
26260 
26261  if (config.playback.format == ma_format_unknown) {
26262  config.playback.format = MA_DEFAULT_FORMAT;
26263  pDevice->playback.usingDefaultFormat = MA_TRUE;
26264  }
26265  if (config.playback.channels == 0) {
26266  config.playback.channels = MA_DEFAULT_CHANNELS;
26267  pDevice->playback.usingDefaultChannels = MA_TRUE;
26268  }
26269  if (config.playback.channelMap[0] == MA_CHANNEL_NONE) {
26270  pDevice->playback.usingDefaultChannelMap = MA_TRUE;
26271  }
26272 
26273 
26274  /* Default buffer size. */
26275  if (config.bufferSizeInMilliseconds == 0 && config.bufferSizeInFrames == 0) {
26276  config.bufferSizeInMilliseconds = (config.performanceProfile == ma_performance_profile_low_latency) ? MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY : MA_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE;
26277  pDevice->usingDefaultBufferSize = MA_TRUE;
26278  }
26279 
26280  /* Default periods. */
26281  if (config.periods == 0) {
26282  config.periods = MA_DEFAULT_PERIODS;
26283  pDevice->usingDefaultPeriods = MA_TRUE;
26284  }
26285 
26286  /*
26287  Must have at least 3 periods for full-duplex mode. The idea is that the playback and capture positions hang out in the middle period, with the surrounding
26288  periods acting as a buffer in case the capture and playback devices get's slightly out of sync.
26289  */
26290  if (config.deviceType == ma_device_type_duplex && config.periods < 3) {
26291  config.periods = 3;
26292  }
26293 
26294 
26295  pDevice->type = config.deviceType;
26296  pDevice->sampleRate = config.sampleRate;
26297 
26298  pDevice->capture.shareMode = config.capture.shareMode;
26299  pDevice->capture.format = config.capture.format;
26300  pDevice->capture.channels = config.capture.channels;
26301  ma_channel_map_copy(pDevice->capture.channelMap, config.capture.channelMap, config.capture.channels);
26302 
26303  pDevice->playback.shareMode = config.playback.shareMode;
26304  pDevice->playback.format = config.playback.format;
26305  pDevice->playback.channels = config.playback.channels;
26306  ma_channel_map_copy(pDevice->playback.channelMap, config.playback.channelMap, config.playback.channels);
26307 
26308 
26309  /* The internal format, channel count and sample rate can be modified by the backend. */
26310  pDevice->capture.internalFormat = pDevice->capture.format;
26311  pDevice->capture.internalChannels = pDevice->capture.channels;
26312  pDevice->capture.internalSampleRate = pDevice->sampleRate;
26313  ma_channel_map_copy(pDevice->capture.internalChannelMap, pDevice->capture.channelMap, pDevice->capture.channels);
26314 
26315  pDevice->playback.internalFormat = pDevice->playback.format;
26316  pDevice->playback.internalChannels = pDevice->playback.channels;
26317  pDevice->playback.internalSampleRate = pDevice->sampleRate;
26318  ma_channel_map_copy(pDevice->playback.internalChannelMap, pDevice->playback.channelMap, pDevice->playback.channels);
26319 
26320 
26321  if (ma_mutex_init(pContext, &pDevice->lock) != MA_SUCCESS) {
26322  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create mutex.", MA_FAILED_TO_CREATE_MUTEX);
26323  }
26324 
26325  /*
26326  When the device is started, the worker thread is the one that does the actual startup of the backend device. We
26327  use a semaphore to wait for the background thread to finish the work. The same applies for stopping the device.
26328 
26329  Each of these semaphores is released internally by the worker thread when the work is completed. The start
26330  semaphore is also used to wake up the worker thread.
26331  */
26332  if (ma_event_init(pContext, &pDevice->wakeupEvent) != MA_SUCCESS) {
26333  ma_mutex_uninit(&pDevice->lock);
26334  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread wakeup event.", MA_FAILED_TO_CREATE_EVENT);
26335  }
26336  if (ma_event_init(pContext, &pDevice->startEvent) != MA_SUCCESS) {
26337  ma_event_uninit(&pDevice->wakeupEvent);
26338  ma_mutex_uninit(&pDevice->lock);
26339  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread start event.", MA_FAILED_TO_CREATE_EVENT);
26340  }
26341  if (ma_event_init(pContext, &pDevice->stopEvent) != MA_SUCCESS) {
26342  ma_event_uninit(&pDevice->startEvent);
26343  ma_event_uninit(&pDevice->wakeupEvent);
26344  ma_mutex_uninit(&pDevice->lock);
26345  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread stop event.", MA_FAILED_TO_CREATE_EVENT);
26346  }
26347 
26348 
26349  result = pContext->onDeviceInit(pContext, &config, pDevice);
26350  if (result != MA_SUCCESS) {
26351  return MA_NO_BACKEND; /* The error message will have been posted with ma_post_error() by the source of the error so don't bother calling it here. */
26352  }
26353 
26354  ma_device__post_init_setup(pDevice, pConfig->deviceType);
26355 
26356 
26357  /* If the backend did not fill out a name for the device, try a generic method. */
26358  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26359  if (pDevice->capture.name[0] == '\0') {
26360  if (ma_context__try_get_device_name_by_id(pContext, ma_device_type_capture, config.capture.pDeviceID, pDevice->capture.name, sizeof(pDevice->capture.name)) != MA_SUCCESS) {
26361  ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), (config.capture.pDeviceID == NULL) ? MA_DEFAULT_CAPTURE_DEVICE_NAME : "Capture Device", (size_t)-1);
26362  }
26363  }
26364  }
26365  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
26366  if (pDevice->playback.name[0] == '\0') {
26367  if (ma_context__try_get_device_name_by_id(pContext, ma_device_type_playback, config.playback.pDeviceID, pDevice->playback.name, sizeof(pDevice->playback.name)) != MA_SUCCESS) {
26368  ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), (config.playback.pDeviceID == NULL) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : "Playback Device", (size_t)-1);
26369  }
26370  }
26371  }
26372 
26373 
26374  /* Some backends don't require the worker thread. */
26375  if (!ma_context_is_backend_asynchronous(pContext)) {
26376  /* The worker thread. */
26377  if (ma_thread_create(pContext, &pDevice->thread, ma_worker_thread, pDevice) != MA_SUCCESS) {
26378  ma_device_uninit(pDevice);
26379  return ma_context_post_error(pContext, NULL, MA_LOG_LEVEL_ERROR, "Failed to create worker thread.", MA_FAILED_TO_CREATE_THREAD);
26380  }
26381 
26382  /* Wait for the worker thread to put the device into it's stopped state for real. */
26383  ma_event_wait(&pDevice->stopEvent);
26384  } else {
26385  ma_device__set_state(pDevice, MA_STATE_STOPPED);
26386  }
26387 
26388 
26389 #ifdef MA_DEBUG_OUTPUT
26390  printf("[%s]\n", ma_get_backend_name(pDevice->pContext->backend));
26391  if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
26392  printf(" %s (%s)\n", pDevice->capture.name, "Capture");
26393  printf(" Format: %s -> %s\n", ma_get_format_name(pDevice->capture.format), ma_get_format_name(pDevice->capture.internalFormat));
26394  printf(" Channels: %d -> %d\n", pDevice->capture.channels, pDevice->capture.internalChannels);
26395  printf(" Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->capture.internalSampleRate);
26396  printf(" Buffer Size: %d/%d (%d)\n", pDevice->capture.internalBufferSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalBufferSizeInFrames / pDevice->capture.internalPeriods));
26397  printf(" Conversion:\n");
26398  printf(" Pre Format Conversion: %s\n", pDevice->capture.converter.isPreFormatConversionRequired ? "YES" : "NO");
26399  printf(" Post Format Conversion: %s\n", pDevice->capture.converter.isPostFormatConversionRequired ? "YES" : "NO");
26400  printf(" Channel Routing: %s\n", pDevice->capture.converter.isChannelRoutingRequired ? "YES" : "NO");
26401  printf(" SRC: %s\n", pDevice->capture.converter.isSRCRequired ? "YES" : "NO");
26402  printf(" Channel Routing at Start: %s\n", pDevice->capture.converter.isChannelRoutingAtStart ? "YES" : "NO");
26403  printf(" Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO");
26404  }
26405  if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
26406  printf(" %s (%s)\n", pDevice->playback.name, "Playback");
26407  printf(" Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat));
26408  printf(" Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels);
26409  printf(" Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate);
26410  printf(" Buffer Size: %d/%d (%d)\n", pDevice->playback.internalBufferSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalBufferSizeInFrames / pDevice->playback.internalPeriods));
26411  printf(" Conversion:\n");
26412  printf(" Pre Format Conversion: %s\n", pDevice->playback.converter.isPreFormatConversionRequired ? "YES" : "NO");
26413  printf(" Post Format Conversion: %s\n", pDevice->playback.converter.isPostFormatConversionRequired ? "YES" : "NO");
26414  printf(" Channel Routing: %s\n", pDevice->playback.converter.isChannelRoutingRequired ? "YES" : "NO");
26415  printf(" SRC: %s\n", pDevice->playback.converter.isSRCRequired ? "YES" : "NO");
26416  printf(" Channel Routing at Start: %s\n", pDevice->playback.converter.isChannelRoutingAtStart ? "YES" : "NO");
26417  printf(" Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO");
26418  }
26419 #endif
26420 
26421 
26422  ma_assert(ma_device__get_state(pDevice) == MA_STATE_STOPPED);
26423  return MA_SUCCESS;
26424 }
26425 
26426 ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice)
26427 {
26428  ma_result result;
26429  ma_context* pContext;
26430  ma_backend defaultBackends[ma_backend_null+1];
26431  ma_uint32 iBackend;
26432  ma_backend* pBackendsToIterate;
26433  ma_uint32 backendsToIterateCount;
26434 
26435  if (pConfig == NULL) {
26436  return MA_INVALID_ARGS;
26437  }
26438 
26439  pContext = (ma_context*)ma_malloc(sizeof(*pContext));
26440  if (pContext == NULL) {
26441  return MA_OUT_OF_MEMORY;
26442  }
26443 
26444  for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
26445  defaultBackends[iBackend] = (ma_backend)iBackend;
26446  }
26447 
26448  pBackendsToIterate = (ma_backend*)backends;
26449  backendsToIterateCount = backendCount;
26450  if (pBackendsToIterate == NULL) {
26451  pBackendsToIterate = (ma_backend*)defaultBackends;
26452  backendsToIterateCount = ma_countof(defaultBackends);
26453  }
26454 
26456 
26457  for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
26458  result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext);
26459  if (result == MA_SUCCESS) {
26460  result = ma_device_init(pContext, pConfig, pDevice);
26461  if (result == MA_SUCCESS) {
26462  break; /* Success. */
26463  } else {
26464  ma_context_uninit(pContext); /* Failure. */
26465  }
26466  }
26467  }
26468 
26469  if (result != MA_SUCCESS) {
26470  ma_free(pContext);
26471  return result;
26472  }
26473 
26474  pDevice->isOwnerOfContext = MA_TRUE;
26475  return result;
26476 }
26477 
26478 void ma_device_uninit(ma_device* pDevice)
26479 {
26480  if (!ma_device__is_initialized(pDevice)) {
26481  return;
26482  }
26483 
26484  /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */
26485  if (ma_device_is_started(pDevice)) {
26486  ma_device_stop(pDevice);
26487  }
26488 
26489  /* Putting the device into an uninitialized state will make the worker thread return. */
26490  ma_device__set_state(pDevice, MA_STATE_UNINITIALIZED);
26491 
26492  /* Wake up the worker thread and wait for it to properly terminate. */
26493  if (!ma_context_is_backend_asynchronous(pDevice->pContext)) {
26494  ma_event_signal(&pDevice->wakeupEvent);
26495  ma_thread_wait(&pDevice->thread);
26496  }
26497 
26498  pDevice->pContext->onDeviceUninit(pDevice);
26499 
26500  ma_event_uninit(&pDevice->stopEvent);
26501  ma_event_uninit(&pDevice->startEvent);
26502  ma_event_uninit(&pDevice->wakeupEvent);
26503  ma_mutex_uninit(&pDevice->lock);
26504 
26505  if (pDevice->isOwnerOfContext) {
26506  ma_context_uninit(pDevice->pContext);
26507  ma_free(pDevice->pContext);
26508  }
26509 
26510  ma_zero_object(pDevice);
26511 }
26512 
26514 {
26515  if (pDevice == NULL) {
26516  return;
26517  }
26518 
26519  ma_atomic_exchange_ptr(&pDevice->onStop, proc);
26520 }
26521 
26523 {
26524  ma_result result;
26525 
26526  if (pDevice == NULL) {
26527  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
26528  }
26529 
26530  if (ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED) {
26531  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_start() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED);
26532  }
26533 
26534  if (ma_device__get_state(pDevice) == MA_STATE_STARTED) {
26535  return ma_post_error(pDevice, MA_LOG_LEVEL_WARNING, "ma_device_start() called when the device is already started.", MA_INVALID_OPERATION); /* Already started. Returning an error to let the application know because it probably means they're doing something wrong. */
26536  }
26537 
26538  result = MA_ERROR;
26539  ma_mutex_lock(&pDevice->lock);
26540  {
26541  /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a stopped or paused state. */
26542  ma_assert(ma_device__get_state(pDevice) == MA_STATE_STOPPED);
26543 
26544  ma_device__set_state(pDevice, MA_STATE_STARTING);
26545 
26546  /* Asynchronous backends need to be handled differently. */
26547  if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
26548  result = pDevice->pContext->onDeviceStart(pDevice);
26549  if (result == MA_SUCCESS) {
26550  ma_device__set_state(pDevice, MA_STATE_STARTED);
26551  }
26552  } else {
26553  /*
26554  Synchronous backends are started by signaling an event that's being waited on in the worker thread. We first wake up the
26555  thread and then wait for the start event.
26556  */
26557  ma_event_signal(&pDevice->wakeupEvent);
26558 
26559  /*
26560  Wait for the worker thread to finish starting the device. Note that the worker thread will be the one who puts the device
26561  into the started state. Don't call ma_device__set_state() here.
26562  */
26563  ma_event_wait(&pDevice->startEvent);
26564  result = pDevice->workResult;
26565  }
26566  }
26567  ma_mutex_unlock(&pDevice->lock);
26568 
26569  return result;
26570 }
26571 
26573 {
26574  ma_result result;
26575 
26576  if (pDevice == NULL) {
26577  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_stop() called with invalid arguments (pDevice == NULL).", MA_INVALID_ARGS);
26578  }
26579 
26580  if (ma_device__get_state(pDevice) == MA_STATE_UNINITIALIZED) {
26581  return ma_post_error(pDevice, MA_LOG_LEVEL_ERROR, "ma_device_stop() called for an uninitialized device.", MA_DEVICE_NOT_INITIALIZED);
26582  }
26583 
26584  if (ma_device__get_state(pDevice) == MA_STATE_STOPPED) {
26585  return ma_post_error(pDevice, MA_LOG_LEVEL_WARNING, "ma_device_stop() called when the device is already stopped.", MA_INVALID_OPERATION); /* Already stopped. Returning an error to let the application know because it probably means they're doing something wrong. */
26586  }
26587 
26588  result = MA_ERROR;
26589  ma_mutex_lock(&pDevice->lock);
26590  {
26591  /* Starting and stopping are wrapped in a mutex which means we can assert that the device is in a started or paused state. */
26592  ma_assert(ma_device__get_state(pDevice) == MA_STATE_STARTED);
26593 
26594  ma_device__set_state(pDevice, MA_STATE_STOPPING);
26595 
26596  /* There's no need to wake up the thread like we do when starting. */
26597 
26598  if (pDevice->pContext->onDeviceStop) {
26599  result = pDevice->pContext->onDeviceStop(pDevice);
26600  } else {
26601  result = MA_SUCCESS;
26602  }
26603 
26604  /* Asynchronous backends need to be handled differently. */
26605  if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
26606  ma_device__set_state(pDevice, MA_STATE_STOPPED);
26607  } else {
26608  /* Synchronous backends. */
26609 
26610  /*
26611  We need to wait for the worker thread to become available for work before returning. Note that the worker thread will be
26612  the one who puts the device into the stopped state. Don't call ma_device__set_state() here.
26613  */
26614  ma_event_wait(&pDevice->stopEvent);
26615  result = MA_SUCCESS;
26616  }
26617  }
26618  ma_mutex_unlock(&pDevice->lock);
26619 
26620  return result;
26621 }
26622 
26624 {
26625  if (pDevice == NULL) {
26626  return MA_FALSE;
26627  }
26628 
26629  return ma_device__get_state(pDevice) == MA_STATE_STARTED;
26630 }
26631 
26632 ma_result ma_device_set_master_volume(ma_device* pDevice, float volume)
26633 {
26634  if (pDevice == NULL) {
26635  return MA_INVALID_ARGS;
26636  }
26637 
26638  if (volume < 0.0f || volume > 1.0f) {
26639  return MA_INVALID_ARGS;
26640  }
26641 
26642  pDevice->masterVolumeFactor = volume;
26643 
26644  return MA_SUCCESS;
26645 }
26646 
26647 ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume)
26648 {
26649  if (pDevice == NULL || pVolume == NULL) {
26650  return MA_INVALID_ARGS;
26651  }
26652 
26653  *pVolume = pDevice->masterVolumeFactor;
26654 
26655  return MA_SUCCESS;
26656 }
26657 
26658 ma_result ma_device_set_master_gain_db(ma_device* pDevice, float gainDB)
26659 {
26660  if (gainDB > 0) {
26661  return MA_INVALID_ARGS;
26662  }
26663 
26664  return ma_device_set_master_volume(pDevice, ma_gain_db_to_factor(gainDB));
26665 }
26666 
26667 ma_result ma_device_get_master_gain_db(ma_device* pDevice, float* pGainDB)
26668 {
26669  float factor;
26670  ma_result result;
26671 
26672  if (pGainDB == NULL) {
26673  return MA_INVALID_ARGS;
26674  }
26675 
26676  result = ma_device_get_master_volume(pDevice, &factor);
26677  if (result != MA_SUCCESS) {
26678  return result;
26679  }
26680 
26681  *pGainDB = ma_factor_to_gain_db(factor);
26682 
26683  return MA_SUCCESS;
26684 }
26685 
26686 
26688 {
26690  ma_zero_object(&config);
26691 
26692  return config;
26693 }
26694 
26696 {
26698  ma_zero_object(&config);
26699  config.deviceType = deviceType;
26700 
26701  return config;
26702 }
26703 #endif /* MA_NO_DEVICE_IO */
26704 
26705 
26706 void ma_get_standard_channel_map_microsoft(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
26707 {
26708  /* Based off the speaker configurations mentioned here: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/content/ksmedia/ns-ksmedia-ksaudio_channel_config */
26709  switch (channels)
26710  {
26711  case 1:
26712  {
26713  channelMap[0] = MA_CHANNEL_MONO;
26714  } break;
26715 
26716  case 2:
26717  {
26718  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26719  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26720  } break;
26721 
26722  case 3: /* Not defined, but best guess. */
26723  {
26724  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26725  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26726  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
26727  } break;
26728 
26729  case 4:
26730  {
26731 #ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP
26732  /* Surround. Using the Surround profile has the advantage of the 3rd channel (MA_CHANNEL_FRONT_CENTER) mapping nicely with higher channel counts. */
26733  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26734  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26735  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
26736  channelMap[3] = MA_CHANNEL_BACK_CENTER;
26737 #else
26738  /* Quad. */
26739  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26740  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26741  channelMap[2] = MA_CHANNEL_BACK_LEFT;
26742  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
26743 #endif
26744  } break;
26745 
26746  case 5: /* Not defined, but best guess. */
26747  {
26748  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26749  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26750  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
26751  channelMap[3] = MA_CHANNEL_BACK_LEFT;
26752  channelMap[4] = MA_CHANNEL_BACK_RIGHT;
26753  } break;
26754 
26755  case 6:
26756  {
26757  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26758  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26759  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
26760  channelMap[3] = MA_CHANNEL_LFE;
26761  channelMap[4] = MA_CHANNEL_SIDE_LEFT;
26762  channelMap[5] = MA_CHANNEL_SIDE_RIGHT;
26763  } break;
26764 
26765  case 7: /* Not defined, but best guess. */
26766  {
26767  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26768  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26769  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
26770  channelMap[3] = MA_CHANNEL_LFE;
26771  channelMap[4] = MA_CHANNEL_BACK_CENTER;
26772  channelMap[5] = MA_CHANNEL_SIDE_LEFT;
26773  channelMap[6] = MA_CHANNEL_SIDE_RIGHT;
26774  } break;
26775 
26776  case 8:
26777  default:
26778  {
26779  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26780  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26781  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
26782  channelMap[3] = MA_CHANNEL_LFE;
26783  channelMap[4] = MA_CHANNEL_BACK_LEFT;
26784  channelMap[5] = MA_CHANNEL_BACK_RIGHT;
26785  channelMap[6] = MA_CHANNEL_SIDE_LEFT;
26786  channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
26787  } break;
26788  }
26789 
26790  /* Remainder. */
26791  if (channels > 8) {
26792  ma_uint32 iChannel;
26793  for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
26794  channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
26795  }
26796  }
26797 }
26798 
26799 void ma_get_standard_channel_map_alsa(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
26800 {
26801  switch (channels)
26802  {
26803  case 1:
26804  {
26805  channelMap[0] = MA_CHANNEL_MONO;
26806  } break;
26807 
26808  case 2:
26809  {
26810  channelMap[0] = MA_CHANNEL_LEFT;
26811  channelMap[1] = MA_CHANNEL_RIGHT;
26812  } break;
26813 
26814  case 3:
26815  {
26816  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26817  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26818  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
26819  } break;
26820 
26821  case 4:
26822  {
26823  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26824  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26825  channelMap[2] = MA_CHANNEL_BACK_LEFT;
26826  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
26827  } break;
26828 
26829  case 5:
26830  {
26831  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26832  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26833  channelMap[2] = MA_CHANNEL_BACK_LEFT;
26834  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
26835  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
26836  } break;
26837 
26838  case 6:
26839  {
26840  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26841  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26842  channelMap[2] = MA_CHANNEL_BACK_LEFT;
26843  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
26844  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
26845  channelMap[5] = MA_CHANNEL_LFE;
26846  } break;
26847 
26848  case 7:
26849  {
26850  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26851  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26852  channelMap[2] = MA_CHANNEL_BACK_LEFT;
26853  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
26854  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
26855  channelMap[5] = MA_CHANNEL_LFE;
26856  channelMap[6] = MA_CHANNEL_BACK_CENTER;
26857  } break;
26858 
26859  case 8:
26860  default:
26861  {
26862  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26863  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26864  channelMap[2] = MA_CHANNEL_BACK_LEFT;
26865  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
26866  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
26867  channelMap[5] = MA_CHANNEL_LFE;
26868  channelMap[6] = MA_CHANNEL_SIDE_LEFT;
26869  channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
26870  } break;
26871  }
26872 
26873  /* Remainder. */
26874  if (channels > 8) {
26875  ma_uint32 iChannel;
26876  for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
26877  channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
26878  }
26879  }
26880 }
26881 
26882 void ma_get_standard_channel_map_rfc3551(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
26883 {
26884  switch (channels)
26885  {
26886  case 1:
26887  {
26888  channelMap[0] = MA_CHANNEL_MONO;
26889  } break;
26890 
26891  case 2:
26892  {
26893  channelMap[0] = MA_CHANNEL_LEFT;
26894  channelMap[1] = MA_CHANNEL_RIGHT;
26895  } break;
26896 
26897  case 3:
26898  {
26899  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26900  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26901  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
26902  } break;
26903 
26904  case 4:
26905  {
26906  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26907  channelMap[1] = MA_CHANNEL_FRONT_CENTER;
26908  channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
26909  channelMap[3] = MA_CHANNEL_BACK_CENTER;
26910  } break;
26911 
26912  case 5:
26913  {
26914  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26915  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26916  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
26917  channelMap[3] = MA_CHANNEL_BACK_LEFT;
26918  channelMap[4] = MA_CHANNEL_BACK_RIGHT;
26919  } break;
26920 
26921  case 6:
26922  {
26923  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26924  channelMap[1] = MA_CHANNEL_SIDE_LEFT;
26925  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
26926  channelMap[3] = MA_CHANNEL_FRONT_RIGHT;
26927  channelMap[4] = MA_CHANNEL_SIDE_RIGHT;
26928  channelMap[5] = MA_CHANNEL_BACK_CENTER;
26929  } break;
26930  }
26931 
26932  /* Remainder. */
26933  if (channels > 8) {
26934  ma_uint32 iChannel;
26935  for (iChannel = 6; iChannel < MA_MAX_CHANNELS; ++iChannel) {
26936  channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
26937  }
26938  }
26939 }
26940 
26941 void ma_get_standard_channel_map_flac(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
26942 {
26943  switch (channels)
26944  {
26945  case 1:
26946  {
26947  channelMap[0] = MA_CHANNEL_MONO;
26948  } break;
26949 
26950  case 2:
26951  {
26952  channelMap[0] = MA_CHANNEL_LEFT;
26953  channelMap[1] = MA_CHANNEL_RIGHT;
26954  } break;
26955 
26956  case 3:
26957  {
26958  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26959  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26960  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
26961  } break;
26962 
26963  case 4:
26964  {
26965  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26966  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26967  channelMap[2] = MA_CHANNEL_BACK_LEFT;
26968  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
26969  } break;
26970 
26971  case 5:
26972  {
26973  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26974  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26975  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
26976  channelMap[3] = MA_CHANNEL_BACK_LEFT;
26977  channelMap[4] = MA_CHANNEL_BACK_RIGHT;
26978  } break;
26979 
26980  case 6:
26981  {
26982  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26983  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26984  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
26985  channelMap[3] = MA_CHANNEL_LFE;
26986  channelMap[4] = MA_CHANNEL_BACK_LEFT;
26987  channelMap[5] = MA_CHANNEL_BACK_RIGHT;
26988  } break;
26989 
26990  case 7:
26991  {
26992  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
26993  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
26994  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
26995  channelMap[3] = MA_CHANNEL_LFE;
26996  channelMap[4] = MA_CHANNEL_BACK_CENTER;
26997  channelMap[5] = MA_CHANNEL_SIDE_LEFT;
26998  channelMap[6] = MA_CHANNEL_SIDE_RIGHT;
26999  } break;
27000 
27001  case 8:
27002  default:
27003  {
27004  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27005  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
27006  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
27007  channelMap[3] = MA_CHANNEL_LFE;
27008  channelMap[4] = MA_CHANNEL_BACK_LEFT;
27009  channelMap[5] = MA_CHANNEL_BACK_RIGHT;
27010  channelMap[6] = MA_CHANNEL_SIDE_LEFT;
27011  channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
27012  } break;
27013  }
27014 
27015  /* Remainder. */
27016  if (channels > 8) {
27017  ma_uint32 iChannel;
27018  for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
27019  channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
27020  }
27021  }
27022 }
27023 
27024 void ma_get_standard_channel_map_vorbis(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
27025 {
27026  /* In Vorbis' type 0 channel mapping, the first two channels are not always the standard left/right - it will have the center speaker where the right usually goes. Why?! */
27027  switch (channels)
27028  {
27029  case 1:
27030  {
27031  channelMap[0] = MA_CHANNEL_MONO;
27032  } break;
27033 
27034  case 2:
27035  {
27036  channelMap[0] = MA_CHANNEL_LEFT;
27037  channelMap[1] = MA_CHANNEL_RIGHT;
27038  } break;
27039 
27040  case 3:
27041  {
27042  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27043  channelMap[1] = MA_CHANNEL_FRONT_CENTER;
27044  channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
27045  } break;
27046 
27047  case 4:
27048  {
27049  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27050  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
27051  channelMap[2] = MA_CHANNEL_BACK_LEFT;
27052  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
27053  } break;
27054 
27055  case 5:
27056  {
27057  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27058  channelMap[1] = MA_CHANNEL_FRONT_CENTER;
27059  channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
27060  channelMap[3] = MA_CHANNEL_BACK_LEFT;
27061  channelMap[4] = MA_CHANNEL_BACK_RIGHT;
27062  } break;
27063 
27064  case 6:
27065  {
27066  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27067  channelMap[1] = MA_CHANNEL_FRONT_CENTER;
27068  channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
27069  channelMap[3] = MA_CHANNEL_BACK_LEFT;
27070  channelMap[4] = MA_CHANNEL_BACK_RIGHT;
27071  channelMap[5] = MA_CHANNEL_LFE;
27072  } break;
27073 
27074  case 7:
27075  {
27076  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27077  channelMap[1] = MA_CHANNEL_FRONT_CENTER;
27078  channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
27079  channelMap[3] = MA_CHANNEL_SIDE_LEFT;
27080  channelMap[4] = MA_CHANNEL_SIDE_RIGHT;
27081  channelMap[5] = MA_CHANNEL_BACK_CENTER;
27082  channelMap[6] = MA_CHANNEL_LFE;
27083  } break;
27084 
27085  case 8:
27086  default:
27087  {
27088  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27089  channelMap[1] = MA_CHANNEL_FRONT_CENTER;
27090  channelMap[2] = MA_CHANNEL_FRONT_RIGHT;
27091  channelMap[3] = MA_CHANNEL_SIDE_LEFT;
27092  channelMap[4] = MA_CHANNEL_SIDE_RIGHT;
27093  channelMap[5] = MA_CHANNEL_BACK_LEFT;
27094  channelMap[6] = MA_CHANNEL_BACK_RIGHT;
27095  channelMap[7] = MA_CHANNEL_LFE;
27096  } break;
27097  }
27098 
27099  /* Remainder. */
27100  if (channels > 8) {
27101  ma_uint32 iChannel;
27102  for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
27103  channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
27104  }
27105  }
27106 }
27107 
27108 void ma_get_standard_channel_map_sound4(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
27109 {
27110  switch (channels)
27111  {
27112  case 1:
27113  {
27114  channelMap[0] = MA_CHANNEL_MONO;
27115  } break;
27116 
27117  case 2:
27118  {
27119  channelMap[0] = MA_CHANNEL_LEFT;
27120  channelMap[1] = MA_CHANNEL_RIGHT;
27121  } break;
27122 
27123  case 3:
27124  {
27125  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27126  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
27127  channelMap[2] = MA_CHANNEL_BACK_CENTER;
27128  } break;
27129 
27130  case 4:
27131  {
27132  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27133  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
27134  channelMap[2] = MA_CHANNEL_BACK_LEFT;
27135  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
27136  } break;
27137 
27138  case 5:
27139  {
27140  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27141  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
27142  channelMap[2] = MA_CHANNEL_BACK_LEFT;
27143  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
27144  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
27145  } break;
27146 
27147  case 6:
27148  {
27149  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27150  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
27151  channelMap[2] = MA_CHANNEL_BACK_LEFT;
27152  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
27153  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
27154  channelMap[5] = MA_CHANNEL_LFE;
27155  } break;
27156 
27157  case 7:
27158  {
27159  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27160  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
27161  channelMap[2] = MA_CHANNEL_BACK_LEFT;
27162  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
27163  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
27164  channelMap[5] = MA_CHANNEL_BACK_CENTER;
27165  channelMap[6] = MA_CHANNEL_LFE;
27166  } break;
27167 
27168  case 8:
27169  default:
27170  {
27171  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27172  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
27173  channelMap[2] = MA_CHANNEL_BACK_LEFT;
27174  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
27175  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
27176  channelMap[5] = MA_CHANNEL_LFE;
27177  channelMap[6] = MA_CHANNEL_SIDE_LEFT;
27178  channelMap[7] = MA_CHANNEL_SIDE_RIGHT;
27179  } break;
27180  }
27181 
27182  /* Remainder. */
27183  if (channels > 8) {
27184  ma_uint32 iChannel;
27185  for (iChannel = 8; iChannel < MA_MAX_CHANNELS; ++iChannel) {
27186  channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-8));
27187  }
27188  }
27189 }
27190 
27191 void ma_get_standard_channel_map_sndio(ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
27192 {
27193  switch (channels)
27194  {
27195  case 1:
27196  {
27197  channelMap[0] = MA_CHANNEL_MONO;
27198  } break;
27199 
27200  case 2:
27201  {
27202  channelMap[0] = MA_CHANNEL_LEFT;
27203  channelMap[1] = MA_CHANNEL_RIGHT;
27204  } break;
27205 
27206  case 3:
27207  {
27208  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27209  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
27210  channelMap[2] = MA_CHANNEL_FRONT_CENTER;
27211  } break;
27212 
27213  case 4:
27214  {
27215  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27216  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
27217  channelMap[2] = MA_CHANNEL_BACK_LEFT;
27218  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
27219  } break;
27220 
27221  case 5:
27222  {
27223  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27224  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
27225  channelMap[2] = MA_CHANNEL_BACK_LEFT;
27226  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
27227  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
27228  } break;
27229 
27230  case 6:
27231  default:
27232  {
27233  channelMap[0] = MA_CHANNEL_FRONT_LEFT;
27234  channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
27235  channelMap[2] = MA_CHANNEL_BACK_LEFT;
27236  channelMap[3] = MA_CHANNEL_BACK_RIGHT;
27237  channelMap[4] = MA_CHANNEL_FRONT_CENTER;
27238  channelMap[5] = MA_CHANNEL_LFE;
27239  } break;
27240  }
27241 
27242  /* Remainder. */
27243  if (channels > 6) {
27244  ma_uint32 iChannel;
27245  for (iChannel = 6; iChannel < MA_MAX_CHANNELS; ++iChannel) {
27246  channelMap[iChannel] = (ma_channel)(MA_CHANNEL_AUX_0 + (iChannel-6));
27247  }
27248  }
27249 }
27250 
27251 void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
27252 {
27253  switch (standardChannelMap)
27254  {
27256  {
27257  ma_get_standard_channel_map_alsa(channels, channelMap);
27258  } break;
27259 
27261  {
27262  ma_get_standard_channel_map_rfc3551(channels, channelMap);
27263  } break;
27264 
27266  {
27267  ma_get_standard_channel_map_flac(channels, channelMap);
27268  } break;
27269 
27271  {
27272  ma_get_standard_channel_map_vorbis(channels, channelMap);
27273  } break;
27274 
27276  {
27277  ma_get_standard_channel_map_sound4(channels, channelMap);
27278  } break;
27279 
27281  {
27282  ma_get_standard_channel_map_sndio(channels, channelMap);
27283  } break;
27284 
27286  default:
27287  {
27288  ma_get_standard_channel_map_microsoft(channels, channelMap);
27289  } break;
27290  }
27291 }
27292 
27293 void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels)
27294 {
27295  if (pOut != NULL && pIn != NULL && channels > 0) {
27296  ma_copy_memory(pOut, pIn, sizeof(*pOut) * channels);
27297  }
27298 }
27299 
27301 {
27302  if (channelMap == NULL) {
27303  return MA_FALSE;
27304  }
27305 
27306  /* A channel count of 0 is invalid. */
27307  if (channels == 0) {
27308  return MA_FALSE;
27309  }
27310 
27311  /* It does not make sense to have a mono channel when there is more than 1 channel. */
27312  if (channels > 1) {
27313  ma_uint32 iChannel;
27314  for (iChannel = 0; iChannel < channels; ++iChannel) {
27315  if (channelMap[iChannel] == MA_CHANNEL_MONO) {
27316  return MA_FALSE;
27317  }
27318  }
27319  }
27320 
27321  return MA_TRUE;
27322 }
27323 
27324 ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel channelMapA[MA_MAX_CHANNELS], const ma_channel channelMapB[MA_MAX_CHANNELS])
27325 {
27326  ma_uint32 iChannel;
27327 
27328  if (channelMapA == channelMapB) {
27329  return MA_FALSE;
27330  }
27331 
27332  if (channels == 0 || channels > MA_MAX_CHANNELS) {
27333  return MA_FALSE;
27334  }
27335 
27336  for (iChannel = 0; iChannel < channels; ++iChannel) {
27337  if (channelMapA[iChannel] != channelMapB[iChannel]) {
27338  return MA_FALSE;
27339  }
27340  }
27341 
27342  return MA_TRUE;
27343 }
27344 
27346 {
27347  ma_uint32 iChannel;
27348 
27349  for (iChannel = 0; iChannel < channels; ++iChannel) {
27350  if (channelMap[iChannel] != MA_CHANNEL_NONE) {
27351  return MA_FALSE;
27352  }
27353  }
27354 
27355  return MA_TRUE;
27356 }
27357 
27359 {
27360  ma_uint32 iChannel;
27361  for (iChannel = 0; iChannel < channels; ++iChannel) {
27362  if (channelMap[iChannel] == channelPosition) {
27363  return MA_TRUE;
27364  }
27365  }
27366 
27367  return MA_FALSE;
27368 }
27369 
27370 
27371 
27372 
27373 /**************************************************************************************************************************************************************
27374 
27375 Format Conversion.
27376 
27377 **************************************************************************************************************************************************************/
27378 void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes)
27379 {
27380 #if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
27381  ma_copy_memory(dst, src, (size_t)sizeInBytes);
27382 #else
27383  while (sizeInBytes > 0) {
27384  ma_uint64 bytesToCopyNow = sizeInBytes;
27385  if (bytesToCopyNow > MA_SIZE_MAX) {
27386  bytesToCopyNow = MA_SIZE_MAX;
27387  }
27388 
27389  ma_copy_memory(dst, src, (size_t)bytesToCopyNow); /* Safe cast to size_t. */
27390 
27391  sizeInBytes -= bytesToCopyNow;
27392  dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow);
27393  src = (const void*)((const ma_uint8*)src + bytesToCopyNow);
27394  }
27395 #endif
27396 }
27397 
27398 void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes)
27399 {
27400 #if 0xFFFFFFFFFFFFFFFF <= MA_SIZE_MAX
27401  ma_zero_memory(dst, (size_t)sizeInBytes);
27402 #else
27403  while (sizeInBytes > 0) {
27404  ma_uint64 bytesToZeroNow = sizeInBytes;
27405  if (bytesToZeroNow > MA_SIZE_MAX) {
27406  bytesToZeroNow = MA_SIZE_MAX;
27407  }
27408 
27409  ma_zero_memory(dst, (size_t)bytesToZeroNow); /* Safe cast to size_t. */
27410 
27411  sizeInBytes -= bytesToZeroNow;
27412  dst = (void*)((ma_uint8*)dst + bytesToZeroNow);
27413  }
27414 #endif
27415 }
27416 
27417 
27418 /* u8 */
27419 void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27420 {
27421  (void)ditherMode;
27422  ma_copy_memory_64(dst, src, count * sizeof(ma_uint8));
27423 }
27424 
27425 
27426 void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27427 {
27428  ma_int16* dst_s16 = (ma_int16*)dst;
27429  const ma_uint8* src_u8 = (const ma_uint8*)src;
27430 
27431  ma_uint64 i;
27432  for (i = 0; i < count; i += 1) {
27433  ma_int16 x = src_u8[i];
27434  x = x - 128;
27435  x = x << 8;
27436  dst_s16[i] = x;
27437  }
27438 
27439  (void)ditherMode;
27440 }
27441 
27442 void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27443 {
27444  ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
27445 }
27446 
27447 #if defined(MA_SUPPORT_SSE2)
27448 void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27449 {
27450  ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
27451 }
27452 #endif
27453 #if defined(MA_SUPPORT_AVX2)
27454 void ma_pcm_u8_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27455 {
27456  ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
27457 }
27458 #endif
27459 #if defined(MA_SUPPORT_AVX512)
27460 void ma_pcm_u8_to_s16__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27461 {
27462  ma_pcm_u8_to_s16__avx2(dst, src, count, ditherMode);
27463 }
27464 #endif
27465 #if defined(MA_SUPPORT_NEON)
27466 void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27467 {
27468  ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
27469 }
27470 #endif
27471 
27472 void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27473 {
27474 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
27475  ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
27476 #else
27477  ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
27478 #endif
27479 }
27480 
27481 
27482 void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27483 {
27484  ma_uint8* dst_s24 = (ma_uint8*)dst;
27485  const ma_uint8* src_u8 = (const ma_uint8*)src;
27486 
27487  ma_uint64 i;
27488  for (i = 0; i < count; i += 1) {
27489  ma_int16 x = src_u8[i];
27490  x = x - 128;
27491 
27492  dst_s24[i*3+0] = 0;
27493  dst_s24[i*3+1] = 0;
27494  dst_s24[i*3+2] = (ma_uint8)((ma_int8)x);
27495  }
27496 
27497  (void)ditherMode;
27498 }
27499 
27500 void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27501 {
27502  ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
27503 }
27504 
27505 #if defined(MA_SUPPORT_SSE2)
27506 void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27507 {
27508  ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
27509 }
27510 #endif
27511 #if defined(MA_SUPPORT_AVX2)
27512 void ma_pcm_u8_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27513 {
27514  ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
27515 }
27516 #endif
27517 #if defined(MA_SUPPORT_AVX512)
27518 void ma_pcm_u8_to_s24__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27519 {
27520  ma_pcm_u8_to_s24__avx2(dst, src, count, ditherMode);
27521 }
27522 #endif
27523 #if defined(MA_SUPPORT_NEON)
27524 void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27525 {
27526  ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
27527 }
27528 #endif
27529 
27530 void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27531 {
27532 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
27533  ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
27534 #else
27535  ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
27536 #endif
27537 }
27538 
27539 
27540 void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27541 {
27542  ma_int32* dst_s32 = (ma_int32*)dst;
27543  const ma_uint8* src_u8 = (const ma_uint8*)src;
27544 
27545  ma_uint64 i;
27546  for (i = 0; i < count; i += 1) {
27547  ma_int32 x = src_u8[i];
27548  x = x - 128;
27549  x = x << 24;
27550  dst_s32[i] = x;
27551  }
27552 
27553  (void)ditherMode;
27554 }
27555 
27556 void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27557 {
27558  ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
27559 }
27560 
27561 #if defined(MA_SUPPORT_SSE2)
27562 void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27563 {
27564  ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
27565 }
27566 #endif
27567 #if defined(MA_SUPPORT_AVX2)
27568 void ma_pcm_u8_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27569 {
27570  ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
27571 }
27572 #endif
27573 #if defined(MA_SUPPORT_AVX512)
27574 void ma_pcm_u8_to_s32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27575 {
27576  ma_pcm_u8_to_s32__avx2(dst, src, count, ditherMode);
27577 }
27578 #endif
27579 #if defined(MA_SUPPORT_NEON)
27580 void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27581 {
27582  ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
27583 }
27584 #endif
27585 
27586 void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27587 {
27588 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
27589  ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
27590 #else
27591  ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
27592 #endif
27593 }
27594 
27595 
27596 void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27597 {
27598  float* dst_f32 = (float*)dst;
27599  const ma_uint8* src_u8 = (const ma_uint8*)src;
27600 
27601  ma_uint64 i;
27602  for (i = 0; i < count; i += 1) {
27603  float x = (float)src_u8[i];
27604  x = x * 0.00784313725490196078f; /* 0..255 to 0..2 */
27605  x = x - 1; /* 0..2 to -1..1 */
27606 
27607  dst_f32[i] = x;
27608  }
27609 
27610  (void)ditherMode;
27611 }
27612 
27613 void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27614 {
27615  ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
27616 }
27617 
27618 #if defined(MA_SUPPORT_SSE2)
27619 void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27620 {
27621  ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
27622 }
27623 #endif
27624 #if defined(MA_SUPPORT_AVX2)
27625 void ma_pcm_u8_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27626 {
27627  ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
27628 }
27629 #endif
27630 #if defined(MA_SUPPORT_AVX512)
27631 void ma_pcm_u8_to_f32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27632 {
27633  ma_pcm_u8_to_f32__avx2(dst, src, count, ditherMode);
27634 }
27635 #endif
27636 #if defined(MA_SUPPORT_NEON)
27637 void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27638 {
27639  ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
27640 }
27641 #endif
27642 
27643 void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27644 {
27645 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
27646  ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
27647 #else
27648  ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
27649 #endif
27650 }
27651 
27652 
27653 
27654 void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
27655 {
27656  ma_uint8* dst_u8 = (ma_uint8*)dst;
27657  const ma_uint8** src_u8 = (const ma_uint8**)src;
27658 
27659  ma_uint64 iFrame;
27660  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
27661  ma_uint32 iChannel;
27662  for (iChannel = 0; iChannel < channels; iChannel += 1) {
27663  dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
27664  }
27665  }
27666 }
27667 
27668 void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
27669 {
27670  ma_uint8* dst_u8 = (ma_uint8*)dst;
27671  const ma_uint8** src_u8 = (const ma_uint8**)src;
27672 
27673  if (channels == 1) {
27674  ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8));
27675  } else if (channels == 2) {
27676  ma_uint64 iFrame;
27677  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
27678  dst_u8[iFrame*2 + 0] = src_u8[0][iFrame];
27679  dst_u8[iFrame*2 + 1] = src_u8[1][iFrame];
27680  }
27681  } else {
27682  ma_uint64 iFrame;
27683  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
27684  ma_uint32 iChannel;
27685  for (iChannel = 0; iChannel < channels; iChannel += 1) {
27686  dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
27687  }
27688  }
27689  }
27690 }
27691 
27692 void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
27693 {
27694 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
27695  ma_pcm_interleave_u8__reference(dst, src, frameCount, channels);
27696 #else
27697  ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels);
27698 #endif
27699 }
27700 
27701 
27702 void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
27703 {
27704  ma_uint8** dst_u8 = (ma_uint8**)dst;
27705  const ma_uint8* src_u8 = (const ma_uint8*)src;
27706 
27707  ma_uint64 iFrame;
27708  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
27709  ma_uint32 iChannel;
27710  for (iChannel = 0; iChannel < channels; iChannel += 1) {
27711  dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel];
27712  }
27713  }
27714 }
27715 
27716 void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
27717 {
27718  ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
27719 }
27720 
27721 void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
27722 {
27723 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
27724  ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
27725 #else
27726  ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels);
27727 #endif
27728 }
27729 
27730 
27731 /* s16 */
27732 void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27733 {
27734  ma_uint8* dst_u8 = (ma_uint8*)dst;
27735  const ma_int16* src_s16 = (const ma_int16*)src;
27736 
27737  if (ditherMode == ma_dither_mode_none) {
27738  ma_uint64 i;
27739  for (i = 0; i < count; i += 1) {
27740  ma_int16 x = src_s16[i];
27741  x = x >> 8;
27742  x = x + 128;
27743  dst_u8[i] = (ma_uint8)x;
27744  }
27745  } else {
27746  ma_uint64 i;
27747  for (i = 0; i < count; i += 1) {
27748  ma_int16 x = src_s16[i];
27749 
27750  /* Dither. Don't overflow. */
27751  ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F);
27752  if ((x + dither) <= 0x7FFF) {
27753  x = (ma_int16)(x + dither);
27754  } else {
27755  x = 0x7FFF;
27756  }
27757 
27758  x = x >> 8;
27759  x = x + 128;
27760  dst_u8[i] = (ma_uint8)x;
27761  }
27762  }
27763 }
27764 
27765 void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27766 {
27767  ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
27768 }
27769 
27770 #if defined(MA_SUPPORT_SSE2)
27771 void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27772 {
27773  ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
27774 }
27775 #endif
27776 #if defined(MA_SUPPORT_AVX2)
27777 void ma_pcm_s16_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27778 {
27779  ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
27780 }
27781 #endif
27782 #if defined(MA_SUPPORT_AVX512)
27783 void ma_pcm_s16_to_u8__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27784 {
27785  ma_pcm_s16_to_u8__avx2(dst, src, count, ditherMode);
27786 }
27787 #endif
27788 #if defined(MA_SUPPORT_NEON)
27789 void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27790 {
27791  ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
27792 }
27793 #endif
27794 
27795 void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27796 {
27797 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
27798  ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
27799 #else
27800  ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
27801 #endif
27802 }
27803 
27804 
27805 void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27806 {
27807  (void)ditherMode;
27808  ma_copy_memory_64(dst, src, count * sizeof(ma_int16));
27809 }
27810 
27811 
27812 void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27813 {
27814  ma_uint8* dst_s24 = (ma_uint8*)dst;
27815  const ma_int16* src_s16 = (const ma_int16*)src;
27816 
27817  ma_uint64 i;
27818  for (i = 0; i < count; i += 1) {
27819  dst_s24[i*3+0] = 0;
27820  dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF);
27821  dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8);
27822  }
27823 
27824  (void)ditherMode;
27825 }
27826 
27827 void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27828 {
27829  ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
27830 }
27831 
27832 #if defined(MA_SUPPORT_SSE2)
27833 void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27834 {
27835  ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
27836 }
27837 #endif
27838 #if defined(MA_SUPPORT_AVX2)
27839 void ma_pcm_s16_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27840 {
27841  ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
27842 }
27843 #endif
27844 #if defined(MA_SUPPORT_AVX512)
27845 void ma_pcm_s16_to_s24__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27846 {
27847  ma_pcm_s16_to_s24__avx2(dst, src, count, ditherMode);
27848 }
27849 #endif
27850 #if defined(MA_SUPPORT_NEON)
27851 void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27852 {
27853  ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
27854 }
27855 #endif
27856 
27857 void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27858 {
27859 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
27860  ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
27861 #else
27862  ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
27863 #endif
27864 }
27865 
27866 
27867 void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27868 {
27869  ma_int32* dst_s32 = (ma_int32*)dst;
27870  const ma_int16* src_s16 = (const ma_int16*)src;
27871 
27872  ma_uint64 i;
27873  for (i = 0; i < count; i += 1) {
27874  dst_s32[i] = src_s16[i] << 16;
27875  }
27876 
27877  (void)ditherMode;
27878 }
27879 
27880 void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27881 {
27882  ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
27883 }
27884 
27885 #if defined(MA_SUPPORT_SSE2)
27886 void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27887 {
27888  ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
27889 }
27890 #endif
27891 #if defined(MA_SUPPORT_AVX2)
27892 void ma_pcm_s16_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27893 {
27894  ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
27895 }
27896 #endif
27897 #if defined(MA_SUPPORT_AVX512)
27898 void ma_pcm_s16_to_s32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27899 {
27900  ma_pcm_s16_to_s32__avx2(dst, src, count, ditherMode);
27901 }
27902 #endif
27903 #if defined(MA_SUPPORT_NEON)
27904 void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27905 {
27906  ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
27907 }
27908 #endif
27909 
27910 void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27911 {
27912 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
27913  ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
27914 #else
27915  ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
27916 #endif
27917 }
27918 
27919 
27920 void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27921 {
27922  float* dst_f32 = (float*)dst;
27923  const ma_int16* src_s16 = (const ma_int16*)src;
27924 
27925  ma_uint64 i;
27926  for (i = 0; i < count; i += 1) {
27927  float x = (float)src_s16[i];
27928 
27929 #if 0
27930  /* The accurate way. */
27931  x = x + 32768.0f; /* -32768..32767 to 0..65535 */
27932  x = x * 0.00003051804379339284f; /* 0..65536 to 0..2 */
27933  x = x - 1; /* 0..2 to -1..1 */
27934 #else
27935  /* The fast way. */
27936  x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */
27937 #endif
27938 
27939  dst_f32[i] = x;
27940  }
27941 
27942  (void)ditherMode;
27943 }
27944 
27945 void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27946 {
27947  ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
27948 }
27949 
27950 #if defined(MA_SUPPORT_SSE2)
27951 void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27952 {
27953  ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
27954 }
27955 #endif
27956 #if defined(MA_SUPPORT_AVX2)
27957 void ma_pcm_s16_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27958 {
27959  ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
27960 }
27961 #endif
27962 #if defined(MA_SUPPORT_AVX512)
27963 void ma_pcm_s16_to_f32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27964 {
27965  ma_pcm_s16_to_f32__avx2(dst, src, count, ditherMode);
27966 }
27967 #endif
27968 #if defined(MA_SUPPORT_NEON)
27969 void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27970 {
27971  ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
27972 }
27973 #endif
27974 
27975 void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
27976 {
27977 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
27978  ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
27979 #else
27980  ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
27981 #endif
27982 }
27983 
27984 
27985 void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
27986 {
27987  ma_int16* dst_s16 = (ma_int16*)dst;
27988  const ma_int16** src_s16 = (const ma_int16**)src;
27989 
27990  ma_uint64 iFrame;
27991  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
27992  ma_uint32 iChannel;
27993  for (iChannel = 0; iChannel < channels; iChannel += 1) {
27994  dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame];
27995  }
27996  }
27997 }
27998 
27999 void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
28000 {
28001  ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
28002 }
28003 
28004 void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
28005 {
28006 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
28007  ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
28008 #else
28009  ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels);
28010 #endif
28011 }
28012 
28013 
28014 void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
28015 {
28016  ma_int16** dst_s16 = (ma_int16**)dst;
28017  const ma_int16* src_s16 = (const ma_int16*)src;
28018 
28019  ma_uint64 iFrame;
28020  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
28021  ma_uint32 iChannel;
28022  for (iChannel = 0; iChannel < channels; iChannel += 1) {
28023  dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel];
28024  }
28025  }
28026 }
28027 
28028 void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
28029 {
28030  ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
28031 }
28032 
28033 void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
28034 {
28035 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
28036  ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
28037 #else
28038  ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels);
28039 #endif
28040 }
28041 
28042 
28043 /* s24 */
28044 void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28045 {
28046  ma_uint8* dst_u8 = (ma_uint8*)dst;
28047  const ma_uint8* src_s24 = (const ma_uint8*)src;
28048 
28049  if (ditherMode == ma_dither_mode_none) {
28050  ma_uint64 i;
28051  for (i = 0; i < count; i += 1) {
28052  ma_int8 x = (ma_int8)src_s24[i*3 + 2] + 128;
28053  dst_u8[i] = (ma_uint8)x;
28054  }
28055  } else {
28056  ma_uint64 i;
28057  for (i = 0; i < count; i += 1) {
28058  ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24);
28059 
28060  /* Dither. Don't overflow. */
28061  ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
28062  if ((ma_int64)x + dither <= 0x7FFFFFFF) {
28063  x = x + dither;
28064  } else {
28065  x = 0x7FFFFFFF;
28066  }
28067 
28068  x = x >> 24;
28069  x = x + 128;
28070  dst_u8[i] = (ma_uint8)x;
28071  }
28072  }
28073 }
28074 
28075 void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28076 {
28077  ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
28078 }
28079 
28080 #if defined(MA_SUPPORT_SSE2)
28081 void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28082 {
28083  ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
28084 }
28085 #endif
28086 #if defined(MA_SUPPORT_AVX2)
28087 void ma_pcm_s24_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28088 {
28089  ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
28090 }
28091 #endif
28092 #if defined(MA_SUPPORT_AVX512)
28093 void ma_pcm_s24_to_u8__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28094 {
28095  ma_pcm_s24_to_u8__avx2(dst, src, count, ditherMode);
28096 }
28097 #endif
28098 #if defined(MA_SUPPORT_NEON)
28099 void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28100 {
28101  ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
28102 }
28103 #endif
28104 
28105 void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28106 {
28107 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
28108  ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
28109 #else
28110  ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
28111 #endif
28112 }
28113 
28114 
28115 void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28116 {
28117  ma_int16* dst_s16 = (ma_int16*)dst;
28118  const ma_uint8* src_s24 = (const ma_uint8*)src;
28119 
28120  if (ditherMode == ma_dither_mode_none) {
28121  ma_uint64 i;
28122  for (i = 0; i < count; i += 1) {
28123  ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]);
28124  ma_uint16 dst_hi = ((ma_uint16)src_s24[i*3 + 2]) << 8;
28125  dst_s16[i] = (ma_int16)dst_lo | dst_hi;
28126  }
28127  } else {
28128  ma_uint64 i;
28129  for (i = 0; i < count; i += 1) {
28130  ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24);
28131 
28132  /* Dither. Don't overflow. */
28133  ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
28134  if ((ma_int64)x + dither <= 0x7FFFFFFF) {
28135  x = x + dither;
28136  } else {
28137  x = 0x7FFFFFFF;
28138  }
28139 
28140  x = x >> 16;
28141  dst_s16[i] = (ma_int16)x;
28142  }
28143  }
28144 }
28145 
28146 void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28147 {
28148  ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
28149 }
28150 
28151 #if defined(MA_SUPPORT_SSE2)
28152 void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28153 {
28154  ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
28155 }
28156 #endif
28157 #if defined(MA_SUPPORT_AVX2)
28158 void ma_pcm_s24_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28159 {
28160  ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
28161 }
28162 #endif
28163 #if defined(MA_SUPPORT_AVX512)
28164 void ma_pcm_s24_to_s16__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28165 {
28166  ma_pcm_s24_to_s16__avx2(dst, src, count, ditherMode);
28167 }
28168 #endif
28169 #if defined(MA_SUPPORT_NEON)
28170 void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28171 {
28172  ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
28173 }
28174 #endif
28175 
28176 void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28177 {
28178 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
28179  ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
28180 #else
28181  ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
28182 #endif
28183 }
28184 
28185 
28186 void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28187 {
28188  (void)ditherMode;
28189 
28190  ma_copy_memory_64(dst, src, count * 3);
28191 }
28192 
28193 
28194 void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28195 {
28196  ma_int32* dst_s32 = (ma_int32*)dst;
28197  const ma_uint8* src_s24 = (const ma_uint8*)src;
28198 
28199  ma_uint64 i;
28200  for (i = 0; i < count; i += 1) {
28201  dst_s32[i] = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24);
28202  }
28203 
28204  (void)ditherMode;
28205 }
28206 
28207 void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28208 {
28209  ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
28210 }
28211 
28212 #if defined(MA_SUPPORT_SSE2)
28213 void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28214 {
28215  ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
28216 }
28217 #endif
28218 #if defined(MA_SUPPORT_AVX2)
28219 void ma_pcm_s24_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28220 {
28221  ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
28222 }
28223 #endif
28224 #if defined(MA_SUPPORT_AVX512)
28225 void ma_pcm_s24_to_s32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28226 {
28227  ma_pcm_s24_to_s32__avx2(dst, src, count, ditherMode);
28228 }
28229 #endif
28230 #if defined(MA_SUPPORT_NEON)
28231 void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28232 {
28233  ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
28234 }
28235 #endif
28236 
28237 void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28238 {
28239 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
28240  ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
28241 #else
28242  ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
28243 #endif
28244 }
28245 
28246 
28247 void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28248 {
28249  float* dst_f32 = (float*)dst;
28250  const ma_uint8* src_s24 = (const ma_uint8*)src;
28251 
28252  ma_uint64 i;
28253  for (i = 0; i < count; i += 1) {
28254  float x = (float)(((ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24)) >> 8);
28255 
28256 #if 0
28257  /* The accurate way. */
28258  x = x + 8388608.0f; /* -8388608..8388607 to 0..16777215 */
28259  x = x * 0.00000011920929665621f; /* 0..16777215 to 0..2 */
28260  x = x - 1; /* 0..2 to -1..1 */
28261 #else
28262  /* The fast way. */
28263  x = x * 0.00000011920928955078125f; /* -8388608..8388607 to -1..0.999969482421875 */
28264 #endif
28265 
28266  dst_f32[i] = x;
28267  }
28268 
28269  (void)ditherMode;
28270 }
28271 
28272 void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28273 {
28274  ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
28275 }
28276 
28277 #if defined(MA_SUPPORT_SSE2)
28278 void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28279 {
28280  ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
28281 }
28282 #endif
28283 #if defined(MA_SUPPORT_AVX2)
28284 void ma_pcm_s24_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28285 {
28286  ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
28287 }
28288 #endif
28289 #if defined(MA_SUPPORT_AVX512)
28290 void ma_pcm_s24_to_f32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28291 {
28292  ma_pcm_s24_to_f32__avx2(dst, src, count, ditherMode);
28293 }
28294 #endif
28295 #if defined(MA_SUPPORT_NEON)
28296 void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28297 {
28298  ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
28299 }
28300 #endif
28301 
28302 void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28303 {
28304 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
28305  ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
28306 #else
28307  ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
28308 #endif
28309 }
28310 
28311 
28312 void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
28313 {
28314  ma_uint8* dst8 = (ma_uint8*)dst;
28315  const ma_uint8** src8 = (const ma_uint8**)src;
28316 
28317  ma_uint64 iFrame;
28318  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
28319  ma_uint32 iChannel;
28320  for (iChannel = 0; iChannel < channels; iChannel += 1) {
28321  dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0];
28322  dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1];
28323  dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2];
28324  }
28325  }
28326 }
28327 
28328 void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
28329 {
28330  ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
28331 }
28332 
28333 void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
28334 {
28335 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
28336  ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
28337 #else
28338  ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels);
28339 #endif
28340 }
28341 
28342 
28343 void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
28344 {
28345  ma_uint8** dst8 = (ma_uint8**)dst;
28346  const ma_uint8* src8 = (const ma_uint8*)src;
28347 
28348  ma_uint32 iFrame;
28349  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
28350  ma_uint32 iChannel;
28351  for (iChannel = 0; iChannel < channels; iChannel += 1) {
28352  dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0];
28353  dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1];
28354  dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2];
28355  }
28356  }
28357 }
28358 
28359 void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
28360 {
28361  ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
28362 }
28363 
28364 void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
28365 {
28366 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
28367  ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
28368 #else
28369  ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels);
28370 #endif
28371 }
28372 
28373 
28374 
28375 /* s32 */
28376 void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28377 {
28378  ma_uint8* dst_u8 = (ma_uint8*)dst;
28379  const ma_int32* src_s32 = (const ma_int32*)src;
28380 
28381  if (ditherMode == ma_dither_mode_none) {
28382  ma_uint64 i;
28383  for (i = 0; i < count; i += 1) {
28384  ma_int32 x = src_s32[i];
28385  x = x >> 24;
28386  x = x + 128;
28387  dst_u8[i] = (ma_uint8)x;
28388  }
28389  } else {
28390  ma_uint64 i;
28391  for (i = 0; i < count; i += 1) {
28392  ma_int32 x = src_s32[i];
28393 
28394  /* Dither. Don't overflow. */
28395  ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
28396  if ((ma_int64)x + dither <= 0x7FFFFFFF) {
28397  x = x + dither;
28398  } else {
28399  x = 0x7FFFFFFF;
28400  }
28401 
28402  x = x >> 24;
28403  x = x + 128;
28404  dst_u8[i] = (ma_uint8)x;
28405  }
28406  }
28407 }
28408 
28409 void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28410 {
28411  ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
28412 }
28413 
28414 #if defined(MA_SUPPORT_SSE2)
28415 void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28416 {
28417  ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
28418 }
28419 #endif
28420 #if defined(MA_SUPPORT_AVX2)
28421 void ma_pcm_s32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28422 {
28423  ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
28424 }
28425 #endif
28426 #if defined(MA_SUPPORT_AVX512)
28427 void ma_pcm_s32_to_u8__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28428 {
28429  ma_pcm_s32_to_u8__avx2(dst, src, count, ditherMode);
28430 }
28431 #endif
28432 #if defined(MA_SUPPORT_NEON)
28433 void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28434 {
28435  ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
28436 }
28437 #endif
28438 
28439 void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28440 {
28441 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
28442  ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
28443 #else
28444  ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
28445 #endif
28446 }
28447 
28448 
28449 void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28450 {
28451  ma_int16* dst_s16 = (ma_int16*)dst;
28452  const ma_int32* src_s32 = (const ma_int32*)src;
28453 
28454  if (ditherMode == ma_dither_mode_none) {
28455  ma_uint64 i;
28456  for (i = 0; i < count; i += 1) {
28457  ma_int32 x = src_s32[i];
28458  x = x >> 16;
28459  dst_s16[i] = (ma_int16)x;
28460  }
28461  } else {
28462  ma_uint64 i;
28463  for (i = 0; i < count; i += 1) {
28464  ma_int32 x = src_s32[i];
28465 
28466  /* Dither. Don't overflow. */
28467  ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
28468  if ((ma_int64)x + dither <= 0x7FFFFFFF) {
28469  x = x + dither;
28470  } else {
28471  x = 0x7FFFFFFF;
28472  }
28473 
28474  x = x >> 16;
28475  dst_s16[i] = (ma_int16)x;
28476  }
28477  }
28478 }
28479 
28480 void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28481 {
28482  ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
28483 }
28484 
28485 #if defined(MA_SUPPORT_SSE2)
28486 void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28487 {
28488  ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
28489 }
28490 #endif
28491 #if defined(MA_SUPPORT_AVX2)
28492 void ma_pcm_s32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28493 {
28494  ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
28495 }
28496 #endif
28497 #if defined(MA_SUPPORT_AVX512)
28498 void ma_pcm_s32_to_s16__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28499 {
28500  ma_pcm_s32_to_s16__avx2(dst, src, count, ditherMode);
28501 }
28502 #endif
28503 #if defined(MA_SUPPORT_NEON)
28504 void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28505 {
28506  ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
28507 }
28508 #endif
28509 
28510 void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28511 {
28512 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
28513  ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
28514 #else
28515  ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
28516 #endif
28517 }
28518 
28519 
28520 void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28521 {
28522  ma_uint8* dst_s24 = (ma_uint8*)dst;
28523  const ma_int32* src_s32 = (const ma_int32*)src;
28524 
28525  ma_uint64 i;
28526  for (i = 0; i < count; i += 1) {
28527  ma_uint32 x = (ma_uint32)src_s32[i];
28528  dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8);
28529  dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16);
28530  dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24);
28531  }
28532 
28533  (void)ditherMode; /* No dithering for s32 -> s24. */
28534 }
28535 
28536 void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28537 {
28538  ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
28539 }
28540 
28541 #if defined(MA_SUPPORT_SSE2)
28542 void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28543 {
28544  ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
28545 }
28546 #endif
28547 #if defined(MA_SUPPORT_AVX2)
28548 void ma_pcm_s32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28549 {
28550  ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
28551 }
28552 #endif
28553 #if defined(MA_SUPPORT_AVX512)
28554 void ma_pcm_s32_to_s24__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28555 {
28556  ma_pcm_s32_to_s24__avx2(dst, src, count, ditherMode);
28557 }
28558 #endif
28559 #if defined(MA_SUPPORT_NEON)
28560 void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28561 {
28562  ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
28563 }
28564 #endif
28565 
28566 void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28567 {
28568 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
28569  ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
28570 #else
28571  ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
28572 #endif
28573 }
28574 
28575 
28576 void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28577 {
28578  (void)ditherMode;
28579 
28580  ma_copy_memory_64(dst, src, count * sizeof(ma_int32));
28581 }
28582 
28583 
28584 void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28585 {
28586  float* dst_f32 = (float*)dst;
28587  const ma_int32* src_s32 = (const ma_int32*)src;
28588 
28589  ma_uint64 i;
28590  for (i = 0; i < count; i += 1) {
28591  double x = src_s32[i];
28592 
28593 #if 0
28594  x = x + 2147483648.0;
28595  x = x * 0.0000000004656612873077392578125;
28596  x = x - 1;
28597 #else
28598  x = x / 2147483648.0;
28599 #endif
28600 
28601  dst_f32[i] = (float)x;
28602  }
28603 
28604  (void)ditherMode; /* No dithering for s32 -> f32. */
28605 }
28606 
28607 void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28608 {
28609  ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
28610 }
28611 
28612 #if defined(MA_SUPPORT_SSE2)
28613 void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28614 {
28615  ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
28616 }
28617 #endif
28618 #if defined(MA_SUPPORT_AVX2)
28619 void ma_pcm_s32_to_f32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28620 {
28621  ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
28622 }
28623 #endif
28624 #if defined(MA_SUPPORT_AVX512)
28625 void ma_pcm_s32_to_f32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28626 {
28627  ma_pcm_s32_to_f32__avx2(dst, src, count, ditherMode);
28628 }
28629 #endif
28630 #if defined(MA_SUPPORT_NEON)
28631 void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28632 {
28633  ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
28634 }
28635 #endif
28636 
28637 void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28638 {
28639 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
28640  ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
28641 #else
28642  ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
28643 #endif
28644 }
28645 
28646 
28647 void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
28648 {
28649  ma_int32* dst_s32 = (ma_int32*)dst;
28650  const ma_int32** src_s32 = (const ma_int32**)src;
28651 
28652  ma_uint64 iFrame;
28653  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
28654  ma_uint32 iChannel;
28655  for (iChannel = 0; iChannel < channels; iChannel += 1) {
28656  dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame];
28657  }
28658  }
28659 }
28660 
28661 void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
28662 {
28663  ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
28664 }
28665 
28666 void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
28667 {
28668 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
28669  ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
28670 #else
28671  ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels);
28672 #endif
28673 }
28674 
28675 
28676 void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
28677 {
28678  ma_int32** dst_s32 = (ma_int32**)dst;
28679  const ma_int32* src_s32 = (const ma_int32*)src;
28680 
28681  ma_uint64 iFrame;
28682  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
28683  ma_uint32 iChannel;
28684  for (iChannel = 0; iChannel < channels; iChannel += 1) {
28685  dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel];
28686  }
28687  }
28688 }
28689 
28690 void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
28691 {
28692  ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
28693 }
28694 
28695 void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
28696 {
28697 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
28698  ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
28699 #else
28700  ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels);
28701 #endif
28702 }
28703 
28704 
28705 /* f32 */
28706 void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28707 {
28708  ma_uint64 i;
28709 
28710  ma_uint8* dst_u8 = (ma_uint8*)dst;
28711  const float* src_f32 = (const float*)src;
28712 
28713  float ditherMin = 0;
28714  float ditherMax = 0;
28715  if (ditherMode != ma_dither_mode_none) {
28716  ditherMin = 1.0f / -128;
28717  ditherMax = 1.0f / 127;
28718  }
28719 
28720  for (i = 0; i < count; i += 1) {
28721  float x = src_f32[i];
28722  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
28723  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
28724  x = x + 1; /* -1..1 to 0..2 */
28725  x = x * 127.5f; /* 0..2 to 0..255 */
28726 
28727  dst_u8[i] = (ma_uint8)x;
28728  }
28729 }
28730 
28731 void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28732 {
28733  ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
28734 }
28735 
28736 #if defined(MA_SUPPORT_SSE2)
28737 void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28738 {
28739  ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
28740 }
28741 #endif
28742 #if defined(MA_SUPPORT_AVX2)
28743 void ma_pcm_f32_to_u8__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28744 {
28745  ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
28746 }
28747 #endif
28748 #if defined(MA_SUPPORT_AVX512)
28749 void ma_pcm_f32_to_u8__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28750 {
28751  ma_pcm_f32_to_u8__avx2(dst, src, count, ditherMode);
28752 }
28753 #endif
28754 #if defined(MA_SUPPORT_NEON)
28755 void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28756 {
28757  ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
28758 }
28759 #endif
28760 
28761 void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28762 {
28763 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
28764  ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
28765 #else
28766  ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
28767 #endif
28768 }
28769 
28770 
28771 void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28772 {
28773  ma_uint64 i;
28774 
28775  ma_int16* dst_s16 = (ma_int16*)dst;
28776  const float* src_f32 = (const float*)src;
28777 
28778  float ditherMin = 0;
28779  float ditherMax = 0;
28780  if (ditherMode != ma_dither_mode_none) {
28781  ditherMin = 1.0f / -32768;
28782  ditherMax = 1.0f / 32767;
28783  }
28784 
28785  for (i = 0; i < count; i += 1) {
28786  float x = src_f32[i];
28787  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
28788  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
28789 
28790 #if 0
28791  /* The accurate way. */
28792  x = x + 1; /* -1..1 to 0..2 */
28793  x = x * 32767.5f; /* 0..2 to 0..65535 */
28794  x = x - 32768.0f; /* 0...65535 to -32768..32767 */
28795 #else
28796  /* The fast way. */
28797  x = x * 32767.0f; /* -1..1 to -32767..32767 */
28798 #endif
28799 
28800  dst_s16[i] = (ma_int16)x;
28801  }
28802 }
28803 
28804 void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28805 {
28806  ma_uint64 i;
28807  ma_uint64 i4;
28808  ma_uint64 count4;
28809 
28810  ma_int16* dst_s16 = (ma_int16*)dst;
28811  const float* src_f32 = (const float*)src;
28812 
28813  float ditherMin = 0;
28814  float ditherMax = 0;
28815  if (ditherMode != ma_dither_mode_none) {
28816  ditherMin = 1.0f / -32768;
28817  ditherMax = 1.0f / 32767;
28818  }
28819 
28820  /* Unrolled. */
28821  i = 0;
28822  count4 = count >> 2;
28823  for (i4 = 0; i4 < count4; i4 += 1) {
28824  float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
28825  float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
28826  float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
28827  float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
28828 
28829  float x0 = src_f32[i+0];
28830  float x1 = src_f32[i+1];
28831  float x2 = src_f32[i+2];
28832  float x3 = src_f32[i+3];
28833 
28834  x0 = x0 + d0;
28835  x1 = x1 + d1;
28836  x2 = x2 + d2;
28837  x3 = x3 + d3;
28838 
28839  x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
28840  x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
28841  x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
28842  x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
28843 
28844  x0 = x0 * 32767.0f;
28845  x1 = x1 * 32767.0f;
28846  x2 = x2 * 32767.0f;
28847  x3 = x3 * 32767.0f;
28848 
28849  dst_s16[i+0] = (ma_int16)x0;
28850  dst_s16[i+1] = (ma_int16)x1;
28851  dst_s16[i+2] = (ma_int16)x2;
28852  dst_s16[i+3] = (ma_int16)x3;
28853 
28854  i += 4;
28855  }
28856 
28857  /* Leftover. */
28858  for (; i < count; i += 1) {
28859  float x = src_f32[i];
28860  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
28861  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
28862  x = x * 32767.0f; /* -1..1 to -32767..32767 */
28863 
28864  dst_s16[i] = (ma_int16)x;
28865  }
28866 }
28867 
28868 #if defined(MA_SUPPORT_SSE2)
28869 void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28870 {
28871  ma_uint64 i;
28872  ma_uint64 i8;
28873  ma_uint64 count8;
28874  ma_int16* dst_s16;
28875  const float* src_f32;
28876  float ditherMin;
28877  float ditherMax;
28878 
28879  /* Both the input and output buffers need to be aligned to 16 bytes. */
28880  if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
28881  ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
28882  return;
28883  }
28884 
28885  dst_s16 = (ma_int16*)dst;
28886  src_f32 = (const float*)src;
28887 
28888  ditherMin = 0;
28889  ditherMax = 0;
28890  if (ditherMode != ma_dither_mode_none) {
28891  ditherMin = 1.0f / -32768;
28892  ditherMax = 1.0f / 32767;
28893  }
28894 
28895  i = 0;
28896 
28897  /* SSE2. SSE allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
28898  count8 = count >> 3;
28899  for (i8 = 0; i8 < count8; i8 += 1) {
28900  __m128 d0;
28901  __m128 d1;
28902  __m128 x0;
28903  __m128 x1;
28904 
28905  if (ditherMode == ma_dither_mode_none) {
28906  d0 = _mm_set1_ps(0);
28907  d1 = _mm_set1_ps(0);
28908  } else if (ditherMode == ma_dither_mode_rectangle) {
28909  d0 = _mm_set_ps(
28910  ma_dither_f32_rectangle(ditherMin, ditherMax),
28911  ma_dither_f32_rectangle(ditherMin, ditherMax),
28912  ma_dither_f32_rectangle(ditherMin, ditherMax),
28913  ma_dither_f32_rectangle(ditherMin, ditherMax)
28914  );
28915  d1 = _mm_set_ps(
28916  ma_dither_f32_rectangle(ditherMin, ditherMax),
28917  ma_dither_f32_rectangle(ditherMin, ditherMax),
28918  ma_dither_f32_rectangle(ditherMin, ditherMax),
28919  ma_dither_f32_rectangle(ditherMin, ditherMax)
28920  );
28921  } else {
28922  d0 = _mm_set_ps(
28923  ma_dither_f32_triangle(ditherMin, ditherMax),
28924  ma_dither_f32_triangle(ditherMin, ditherMax),
28925  ma_dither_f32_triangle(ditherMin, ditherMax),
28926  ma_dither_f32_triangle(ditherMin, ditherMax)
28927  );
28928  d1 = _mm_set_ps(
28929  ma_dither_f32_triangle(ditherMin, ditherMax),
28930  ma_dither_f32_triangle(ditherMin, ditherMax),
28931  ma_dither_f32_triangle(ditherMin, ditherMax),
28932  ma_dither_f32_triangle(ditherMin, ditherMax)
28933  );
28934  }
28935 
28936  x0 = *((__m128*)(src_f32 + i) + 0);
28937  x1 = *((__m128*)(src_f32 + i) + 1);
28938 
28939  x0 = _mm_add_ps(x0, d0);
28940  x1 = _mm_add_ps(x1, d1);
28941 
28942  x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f));
28943  x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f));
28944 
28945  _mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1)));
28946 
28947  i += 8;
28948  }
28949 
28950 
28951  /* Leftover. */
28952  for (; i < count; i += 1) {
28953  float x = src_f32[i];
28954  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
28955  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
28956  x = x * 32767.0f; /* -1..1 to -32767..32767 */
28957 
28958  dst_s16[i] = (ma_int16)x;
28959  }
28960 }
28961 #endif
28962 #if defined(MA_SUPPORT_AVX2)
28963 void ma_pcm_f32_to_s16__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
28964 {
28965  ma_uint64 i;
28966  ma_uint64 i16;
28967  ma_uint64 count16;
28968  ma_int16* dst_s16;
28969  const float* src_f32;
28970  float ditherMin;
28971  float ditherMax;
28972 
28973  /* Both the input and output buffers need to be aligned to 32 bytes. */
28974  if ((((ma_uintptr)dst & 31) != 0) || (((ma_uintptr)src & 31) != 0)) {
28975  ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
28976  return;
28977  }
28978 
28979  dst_s16 = (ma_int16*)dst;
28980  src_f32 = (const float*)src;
28981 
28982  ditherMin = 0;
28983  ditherMax = 0;
28984  if (ditherMode != ma_dither_mode_none) {
28985  ditherMin = 1.0f / -32768;
28986  ditherMax = 1.0f / 32767;
28987  }
28988 
28989  i = 0;
28990 
28991  /* AVX2. AVX2 allows us to output 16 s16's at a time which means our loop is unrolled 16 times. */
28992  count16 = count >> 4;
28993  for (i16 = 0; i16 < count16; i16 += 1) {
28994  __m256 d0;
28995  __m256 d1;
28996  __m256 x0;
28997  __m256 x1;
28998  __m256i i0;
28999  __m256i i1;
29000  __m256i p0;
29001  __m256i p1;
29002  __m256i r;
29003 
29004  if (ditherMode == ma_dither_mode_none) {
29005  d0 = _mm256_set1_ps(0);
29006  d1 = _mm256_set1_ps(0);
29007  } else if (ditherMode == ma_dither_mode_rectangle) {
29008  d0 = _mm256_set_ps(
29009  ma_dither_f32_rectangle(ditherMin, ditherMax),
29010  ma_dither_f32_rectangle(ditherMin, ditherMax),
29011  ma_dither_f32_rectangle(ditherMin, ditherMax),
29012  ma_dither_f32_rectangle(ditherMin, ditherMax),
29013  ma_dither_f32_rectangle(ditherMin, ditherMax),
29014  ma_dither_f32_rectangle(ditherMin, ditherMax),
29015  ma_dither_f32_rectangle(ditherMin, ditherMax),
29016  ma_dither_f32_rectangle(ditherMin, ditherMax)
29017  );
29018  d1 = _mm256_set_ps(
29019  ma_dither_f32_rectangle(ditherMin, ditherMax),
29020  ma_dither_f32_rectangle(ditherMin, ditherMax),
29021  ma_dither_f32_rectangle(ditherMin, ditherMax),
29022  ma_dither_f32_rectangle(ditherMin, ditherMax),
29023  ma_dither_f32_rectangle(ditherMin, ditherMax),
29024  ma_dither_f32_rectangle(ditherMin, ditherMax),
29025  ma_dither_f32_rectangle(ditherMin, ditherMax),
29026  ma_dither_f32_rectangle(ditherMin, ditherMax)
29027  );
29028  } else {
29029  d0 = _mm256_set_ps(
29030  ma_dither_f32_triangle(ditherMin, ditherMax),
29031  ma_dither_f32_triangle(ditherMin, ditherMax),
29032  ma_dither_f32_triangle(ditherMin, ditherMax),
29033  ma_dither_f32_triangle(ditherMin, ditherMax),
29034  ma_dither_f32_triangle(ditherMin, ditherMax),
29035  ma_dither_f32_triangle(ditherMin, ditherMax),
29036  ma_dither_f32_triangle(ditherMin, ditherMax),
29037  ma_dither_f32_triangle(ditherMin, ditherMax)
29038  );
29039  d1 = _mm256_set_ps(
29040  ma_dither_f32_triangle(ditherMin, ditherMax),
29041  ma_dither_f32_triangle(ditherMin, ditherMax),
29042  ma_dither_f32_triangle(ditherMin, ditherMax),
29043  ma_dither_f32_triangle(ditherMin, ditherMax),
29044  ma_dither_f32_triangle(ditherMin, ditherMax),
29045  ma_dither_f32_triangle(ditherMin, ditherMax),
29046  ma_dither_f32_triangle(ditherMin, ditherMax),
29047  ma_dither_f32_triangle(ditherMin, ditherMax)
29048  );
29049  }
29050 
29051  x0 = *((__m256*)(src_f32 + i) + 0);
29052  x1 = *((__m256*)(src_f32 + i) + 1);
29053 
29054  x0 = _mm256_add_ps(x0, d0);
29055  x1 = _mm256_add_ps(x1, d1);
29056 
29057  x0 = _mm256_mul_ps(x0, _mm256_set1_ps(32767.0f));
29058  x1 = _mm256_mul_ps(x1, _mm256_set1_ps(32767.0f));
29059 
29060  /* Computing the final result is a little more complicated for AVX2 than SSE2. */
29061  i0 = _mm256_cvttps_epi32(x0);
29062  i1 = _mm256_cvttps_epi32(x1);
29063  p0 = _mm256_permute2x128_si256(i0, i1, 0 | 32);
29064  p1 = _mm256_permute2x128_si256(i0, i1, 1 | 48);
29065  r = _mm256_packs_epi32(p0, p1);
29066 
29067  _mm256_stream_si256(((__m256i*)(dst_s16 + i)), r);
29068 
29069  i += 16;
29070  }
29071 
29072 
29073  /* Leftover. */
29074  for (; i < count; i += 1) {
29075  float x = src_f32[i];
29076  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
29077  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
29078  x = x * 32767.0f; /* -1..1 to -32767..32767 */
29079 
29080  dst_s16[i] = (ma_int16)x;
29081  }
29082 }
29083 #endif
29084 #if defined(MA_SUPPORT_AVX512)
29085 void ma_pcm_f32_to_s16__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29086 {
29087  /* TODO: Convert this from AVX to AVX-512. */
29088  ma_pcm_f32_to_s16__avx2(dst, src, count, ditherMode);
29089 }
29090 #endif
29091 #if defined(MA_SUPPORT_NEON)
29092 void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29093 {
29094  ma_uint64 i;
29095  ma_uint64 i8;
29096  ma_uint64 count8;
29097  ma_int16* dst_s16;
29098  const float* src_f32;
29099  float ditherMin;
29100  float ditherMax;
29101 
29102  /* Both the input and output buffers need to be aligned to 16 bytes. */
29103  if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
29104  ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
29105  return;
29106  }
29107 
29108  dst_s16 = (ma_int16*)dst;
29109  src_f32 = (const float*)src;
29110 
29111  ditherMin = 0;
29112  ditherMax = 0;
29113  if (ditherMode != ma_dither_mode_none) {
29114  ditherMin = 1.0f / -32768;
29115  ditherMax = 1.0f / 32767;
29116  }
29117 
29118  i = 0;
29119 
29120  /* NEON. NEON allows us to output 8 s16's at a time which means our loop is unrolled 8 times. */
29121  count8 = count >> 3;
29122  for (i8 = 0; i8 < count8; i8 += 1) {
29123  float32x4_t d0;
29124  float32x4_t d1;
29125  float32x4_t x0;
29126  float32x4_t x1;
29127  int32x4_t i0;
29128  int32x4_t i1;
29129 
29130  if (ditherMode == ma_dither_mode_none) {
29131  d0 = vmovq_n_f32(0);
29132  d1 = vmovq_n_f32(0);
29133  } else if (ditherMode == ma_dither_mode_rectangle) {
29134  float d0v[4];
29135  d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
29136  d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
29137  d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
29138  d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
29139  d0 = vld1q_f32(d0v);
29140 
29141  float d1v[4];
29142  d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
29143  d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
29144  d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
29145  d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
29146  d1 = vld1q_f32(d1v);
29147  } else {
29148  float d0v[4];
29149  d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
29150  d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
29151  d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
29152  d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
29153  d0 = vld1q_f32(d0v);
29154 
29155  float d1v[4];
29156  d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
29157  d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
29158  d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
29159  d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
29160  d1 = vld1q_f32(d1v);
29161  }
29162 
29163  x0 = *((float32x4_t*)(src_f32 + i) + 0);
29164  x1 = *((float32x4_t*)(src_f32 + i) + 1);
29165 
29166  x0 = vaddq_f32(x0, d0);
29167  x1 = vaddq_f32(x1, d1);
29168 
29169  x0 = vmulq_n_f32(x0, 32767.0f);
29170  x1 = vmulq_n_f32(x1, 32767.0f);
29171 
29172  i0 = vcvtq_s32_f32(x0);
29173  i1 = vcvtq_s32_f32(x1);
29174  *((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1));
29175 
29176  i += 8;
29177  }
29178 
29179 
29180  /* Leftover. */
29181  for (; i < count; i += 1) {
29182  float x = src_f32[i];
29183  x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
29184  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
29185  x = x * 32767.0f; /* -1..1 to -32767..32767 */
29186 
29187  dst_s16[i] = (ma_int16)x;
29188  }
29189 }
29190 #endif
29191 
29192 void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29193 {
29194 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
29195  ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode);
29196 #else
29197  ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
29198 #endif
29199 }
29200 
29201 
29202 void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29203 {
29204  ma_uint8* dst_s24 = (ma_uint8*)dst;
29205  const float* src_f32 = (const float*)src;
29206 
29207  ma_uint64 i;
29208  for (i = 0; i < count; i += 1) {
29209  ma_int32 r;
29210  float x = src_f32[i];
29211  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
29212 
29213 #if 0
29214  /* The accurate way. */
29215  x = x + 1; /* -1..1 to 0..2 */
29216  x = x * 8388607.5f; /* 0..2 to 0..16777215 */
29217  x = x - 8388608.0f; /* 0..16777215 to -8388608..8388607 */
29218 #else
29219  /* The fast way. */
29220  x = x * 8388607.0f; /* -1..1 to -8388607..8388607 */
29221 #endif
29222 
29223  r = (ma_int32)x;
29224  dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0);
29225  dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8);
29226  dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16);
29227  }
29228 
29229  (void)ditherMode; /* No dithering for f32 -> s24. */
29230 }
29231 
29232 void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29233 {
29234  ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
29235 }
29236 
29237 #if defined(MA_SUPPORT_SSE2)
29238 void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29239 {
29240  ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
29241 }
29242 #endif
29243 #if defined(MA_SUPPORT_AVX2)
29244 void ma_pcm_f32_to_s24__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29245 {
29246  ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
29247 }
29248 #endif
29249 #if defined(MA_SUPPORT_AVX512)
29250 void ma_pcm_f32_to_s24__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29251 {
29252  ma_pcm_f32_to_s24__avx2(dst, src, count, ditherMode);
29253 }
29254 #endif
29255 #if defined(MA_SUPPORT_NEON)
29256 void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29257 {
29258  ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
29259 }
29260 #endif
29261 
29262 void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29263 {
29264 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
29265  ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
29266 #else
29267  ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
29268 #endif
29269 }
29270 
29271 
29272 void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29273 {
29274  ma_int32* dst_s32 = (ma_int32*)dst;
29275  const float* src_f32 = (const float*)src;
29276 
29277  ma_uint32 i;
29278  for (i = 0; i < count; i += 1) {
29279  double x = src_f32[i];
29280  x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
29281 
29282 #if 0
29283  /* The accurate way. */
29284  x = x + 1; /* -1..1 to 0..2 */
29285  x = x * 2147483647.5; /* 0..2 to 0..4294967295 */
29286  x = x - 2147483648.0; /* 0...4294967295 to -2147483648..2147483647 */
29287 #else
29288  /* The fast way. */
29289  x = x * 2147483647.0; /* -1..1 to -2147483647..2147483647 */
29290 #endif
29291 
29292  dst_s32[i] = (ma_int32)x;
29293  }
29294 
29295  (void)ditherMode; /* No dithering for f32 -> s32. */
29296 }
29297 
29298 void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29299 {
29300  ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
29301 }
29302 
29303 #if defined(MA_SUPPORT_SSE2)
29304 void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29305 {
29306  ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
29307 }
29308 #endif
29309 #if defined(MA_SUPPORT_AVX2)
29310 void ma_pcm_f32_to_s32__avx2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29311 {
29312  ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
29313 }
29314 #endif
29315 #if defined(MA_SUPPORT_AVX512)
29316 void ma_pcm_f32_to_s32__avx512(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29317 {
29318  ma_pcm_f32_to_s32__avx2(dst, src, count, ditherMode);
29319 }
29320 #endif
29321 #if defined(MA_SUPPORT_NEON)
29322 void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29323 {
29324  ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
29325 }
29326 #endif
29327 
29328 void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29329 {
29330 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
29331  ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
29332 #else
29333  ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
29334 #endif
29335 }
29336 
29337 
29338 void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
29339 {
29340  (void)ditherMode;
29341 
29342  ma_copy_memory_64(dst, src, count * sizeof(float));
29343 }
29344 
29345 
29346 void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
29347 {
29348  float* dst_f32 = (float*)dst;
29349  const float** src_f32 = (const float**)src;
29350 
29351  ma_uint64 iFrame;
29352  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
29353  ma_uint32 iChannel;
29354  for (iChannel = 0; iChannel < channels; iChannel += 1) {
29355  dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame];
29356  }
29357  }
29358 }
29359 
29360 void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
29361 {
29362  ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
29363 }
29364 
29365 void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
29366 {
29367 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
29368  ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
29369 #else
29370  ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels);
29371 #endif
29372 }
29373 
29374 
29375 void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
29376 {
29377  float** dst_f32 = (float**)dst;
29378  const float* src_f32 = (const float*)src;
29379 
29380  ma_uint64 iFrame;
29381  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
29382  ma_uint32 iChannel;
29383  for (iChannel = 0; iChannel < channels; iChannel += 1) {
29384  dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel];
29385  }
29386  }
29387 }
29388 
29389 void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
29390 {
29391  ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
29392 }
29393 
29394 void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
29395 {
29396 #ifdef MA_USE_REFERENCE_CONVERSION_APIS
29397  ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
29398 #else
29399  ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels);
29400 #endif
29401 }
29402 
29403 
29404 void ma_format_converter_init_callbacks__default(ma_format_converter* pConverter)
29405 {
29406  ma_assert(pConverter != NULL);
29407 
29408  switch (pConverter->config.formatIn)
29409  {
29410  case ma_format_u8:
29411  {
29412  if (pConverter->config.formatOut == ma_format_u8) {
29413  pConverter->onConvertPCM = ma_pcm_u8_to_u8;
29414  } else if (pConverter->config.formatOut == ma_format_s16) {
29415  pConverter->onConvertPCM = ma_pcm_u8_to_s16;
29416  } else if (pConverter->config.formatOut == ma_format_s24) {
29417  pConverter->onConvertPCM = ma_pcm_u8_to_s24;
29418  } else if (pConverter->config.formatOut == ma_format_s32) {
29419  pConverter->onConvertPCM = ma_pcm_u8_to_s32;
29420  } else if (pConverter->config.formatOut == ma_format_f32) {
29421  pConverter->onConvertPCM = ma_pcm_u8_to_f32;
29422  }
29423  } break;
29424 
29425  case ma_format_s16:
29426  {
29427  if (pConverter->config.formatOut == ma_format_u8) {
29428  pConverter->onConvertPCM = ma_pcm_s16_to_u8;
29429  } else if (pConverter->config.formatOut == ma_format_s16) {
29430  pConverter->onConvertPCM = ma_pcm_s16_to_s16;
29431  } else if (pConverter->config.formatOut == ma_format_s24) {
29432  pConverter->onConvertPCM = ma_pcm_s16_to_s24;
29433  } else if (pConverter->config.formatOut == ma_format_s32) {
29434  pConverter->onConvertPCM = ma_pcm_s16_to_s32;
29435  } else if (pConverter->config.formatOut == ma_format_f32) {
29436  pConverter->onConvertPCM = ma_pcm_s16_to_f32;
29437  }
29438  } break;
29439 
29440  case ma_format_s24:
29441  {
29442  if (pConverter->config.formatOut == ma_format_u8) {
29443  pConverter->onConvertPCM = ma_pcm_s24_to_u8;
29444  } else if (pConverter->config.formatOut == ma_format_s16) {
29445  pConverter->onConvertPCM = ma_pcm_s24_to_s16;
29446  } else if (pConverter->config.formatOut == ma_format_s24) {
29447  pConverter->onConvertPCM = ma_pcm_s24_to_s24;
29448  } else if (pConverter->config.formatOut == ma_format_s32) {
29449  pConverter->onConvertPCM = ma_pcm_s24_to_s32;
29450  } else if (pConverter->config.formatOut == ma_format_f32) {
29451  pConverter->onConvertPCM = ma_pcm_s24_to_f32;
29452  }
29453  } break;
29454 
29455  case ma_format_s32:
29456  {
29457  if (pConverter->config.formatOut == ma_format_u8) {
29458  pConverter->onConvertPCM = ma_pcm_s32_to_u8;
29459  } else if (pConverter->config.formatOut == ma_format_s16) {
29460  pConverter->onConvertPCM = ma_pcm_s32_to_s16;
29461  } else if (pConverter->config.formatOut == ma_format_s24) {
29462  pConverter->onConvertPCM = ma_pcm_s32_to_s24;
29463  } else if (pConverter->config.formatOut == ma_format_s32) {
29464  pConverter->onConvertPCM = ma_pcm_s32_to_s32;
29465  } else if (pConverter->config.formatOut == ma_format_f32) {
29466  pConverter->onConvertPCM = ma_pcm_s32_to_f32;
29467  }
29468  } break;
29469 
29470  case ma_format_f32:
29471  default:
29472  {
29473  if (pConverter->config.formatOut == ma_format_u8) {
29474  pConverter->onConvertPCM = ma_pcm_f32_to_u8;
29475  } else if (pConverter->config.formatOut == ma_format_s16) {
29476  pConverter->onConvertPCM = ma_pcm_f32_to_s16;
29477  } else if (pConverter->config.formatOut == ma_format_s24) {
29478  pConverter->onConvertPCM = ma_pcm_f32_to_s24;
29479  } else if (pConverter->config.formatOut == ma_format_s32) {
29480  pConverter->onConvertPCM = ma_pcm_f32_to_s32;
29481  } else if (pConverter->config.formatOut == ma_format_f32) {
29482  pConverter->onConvertPCM = ma_pcm_f32_to_f32;
29483  }
29484  } break;
29485  }
29486 }
29487 
29488 #if defined(MA_SUPPORT_SSE2)
29489 void ma_format_converter_init_callbacks__sse2(ma_format_converter* pConverter)
29490 {
29491  ma_assert(pConverter != NULL);
29492 
29493  switch (pConverter->config.formatIn)
29494  {
29495  case ma_format_u8:
29496  {
29497  if (pConverter->config.formatOut == ma_format_u8) {
29498  pConverter->onConvertPCM = ma_pcm_u8_to_u8;
29499  } else if (pConverter->config.formatOut == ma_format_s16) {
29500  pConverter->onConvertPCM = ma_pcm_u8_to_s16__sse2;
29501  } else if (pConverter->config.formatOut == ma_format_s24) {
29502  pConverter->onConvertPCM = ma_pcm_u8_to_s24__sse2;
29503  } else if (pConverter->config.formatOut == ma_format_s32) {
29504  pConverter->onConvertPCM = ma_pcm_u8_to_s32__sse2;
29505  } else if (pConverter->config.formatOut == ma_format_f32) {
29506  pConverter->onConvertPCM = ma_pcm_u8_to_f32__sse2;
29507  }
29508  } break;
29509 
29510  case ma_format_s16:
29511  {
29512  if (pConverter->config.formatOut == ma_format_u8) {
29513  pConverter->onConvertPCM = ma_pcm_s16_to_u8__sse2;
29514  } else if (pConverter->config.formatOut == ma_format_s16) {
29515  pConverter->onConvertPCM = ma_pcm_s16_to_s16;
29516  } else if (pConverter->config.formatOut == ma_format_s24) {
29517  pConverter->onConvertPCM = ma_pcm_s16_to_s24__sse2;
29518  } else if (pConverter->config.formatOut == ma_format_s32) {
29519  pConverter->onConvertPCM = ma_pcm_s16_to_s32__sse2;
29520  } else if (pConverter->config.formatOut == ma_format_f32) {
29521  pConverter->onConvertPCM = ma_pcm_s16_to_f32__sse2;
29522  }
29523  } break;
29524 
29525  case ma_format_s24:
29526  {
29527  if (pConverter->config.formatOut == ma_format_u8) {
29528  pConverter->onConvertPCM = ma_pcm_s24_to_u8__sse2;
29529  } else if (pConverter->config.formatOut == ma_format_s16) {
29530  pConverter->onConvertPCM = ma_pcm_s24_to_s16__sse2;
29531  } else if (pConverter->config.formatOut == ma_format_s24) {
29532  pConverter->onConvertPCM = ma_pcm_s24_to_s24;
29533  } else if (pConverter->config.formatOut == ma_format_s32) {
29534  pConverter->onConvertPCM = ma_pcm_s24_to_s32__sse2;
29535  } else if (pConverter->config.formatOut == ma_format_f32) {
29536  pConverter->onConvertPCM = ma_pcm_s24_to_f32__sse2;
29537  }
29538  } break;
29539 
29540  case ma_format_s32:
29541  {
29542  if (pConverter->config.formatOut == ma_format_u8) {
29543  pConverter->onConvertPCM = ma_pcm_s32_to_u8__sse2;
29544  } else if (pConverter->config.formatOut == ma_format_s16) {
29545  pConverter->onConvertPCM = ma_pcm_s32_to_s16__sse2;
29546  } else if (pConverter->config.formatOut == ma_format_s24) {
29547  pConverter->onConvertPCM = ma_pcm_s32_to_s24__sse2;
29548  } else if (pConverter->config.formatOut == ma_format_s32) {
29549  pConverter->onConvertPCM = ma_pcm_s32_to_s32;
29550  } else if (pConverter->config.formatOut == ma_format_f32) {
29551  pConverter->onConvertPCM = ma_pcm_s32_to_f32__sse2;
29552  }
29553  } break;
29554 
29555  case ma_format_f32:
29556  default:
29557  {
29558  if (pConverter->config.formatOut == ma_format_u8) {
29559  pConverter->onConvertPCM = ma_pcm_f32_to_u8__sse2;
29560  } else if (pConverter->config.formatOut == ma_format_s16) {
29561  pConverter->onConvertPCM = ma_pcm_f32_to_s16__sse2;
29562  } else if (pConverter->config.formatOut == ma_format_s24) {
29563  pConverter->onConvertPCM = ma_pcm_f32_to_s24__sse2;
29564  } else if (pConverter->config.formatOut == ma_format_s32) {
29565  pConverter->onConvertPCM = ma_pcm_f32_to_s32__sse2;
29566  } else if (pConverter->config.formatOut == ma_format_f32) {
29567  pConverter->onConvertPCM = ma_pcm_f32_to_f32;
29568  }
29569  } break;
29570  }
29571 }
29572 #endif
29573 
29574 #if defined(MA_SUPPORT_AVX2)
29575 void ma_format_converter_init_callbacks__avx2(ma_format_converter* pConverter)
29576 {
29577  ma_assert(pConverter != NULL);
29578 
29579  switch (pConverter->config.formatIn)
29580  {
29581  case ma_format_u8:
29582  {
29583  if (pConverter->config.formatOut == ma_format_u8) {
29584  pConverter->onConvertPCM = ma_pcm_u8_to_u8;
29585  } else if (pConverter->config.formatOut == ma_format_s16) {
29586  pConverter->onConvertPCM = ma_pcm_u8_to_s16__avx2;
29587  } else if (pConverter->config.formatOut == ma_format_s24) {
29588  pConverter->onConvertPCM = ma_pcm_u8_to_s24__avx2;
29589  } else if (pConverter->config.formatOut == ma_format_s32) {
29590  pConverter->onConvertPCM = ma_pcm_u8_to_s32__avx2;
29591  } else if (pConverter->config.formatOut == ma_format_f32) {
29592  pConverter->onConvertPCM = ma_pcm_u8_to_f32__avx2;
29593  }
29594  } break;
29595 
29596  case ma_format_s16:
29597  {
29598  if (pConverter->config.formatOut == ma_format_u8) {
29599  pConverter->onConvertPCM = ma_pcm_s16_to_u8__avx2;
29600  } else if (pConverter->config.formatOut == ma_format_s16) {
29601  pConverter->onConvertPCM = ma_pcm_s16_to_s16;
29602  } else if (pConverter->config.formatOut == ma_format_s24) {
29603  pConverter->onConvertPCM = ma_pcm_s16_to_s24__avx2;
29604  } else if (pConverter->config.formatOut == ma_format_s32) {
29605  pConverter->onConvertPCM = ma_pcm_s16_to_s32__avx2;
29606  } else if (pConverter->config.formatOut == ma_format_f32) {
29607  pConverter->onConvertPCM = ma_pcm_s16_to_f32__avx2;
29608  }
29609  } break;
29610 
29611  case ma_format_s24:
29612  {
29613  if (pConverter->config.formatOut == ma_format_u8) {
29614  pConverter->onConvertPCM = ma_pcm_s24_to_u8__avx2;
29615  } else if (pConverter->config.formatOut == ma_format_s16) {
29616  pConverter->onConvertPCM = ma_pcm_s24_to_s16__avx2;
29617  } else if (pConverter->config.formatOut == ma_format_s24) {
29618  pConverter->onConvertPCM = ma_pcm_s24_to_s24;
29619  } else if (pConverter->config.formatOut == ma_format_s32) {
29620  pConverter->onConvertPCM = ma_pcm_s24_to_s32__avx2;
29621  } else if (pConverter->config.formatOut == ma_format_f32) {
29622  pConverter->onConvertPCM = ma_pcm_s24_to_f32__avx2;
29623  }
29624  } break;
29625 
29626  case ma_format_s32:
29627  {
29628  if (pConverter->config.formatOut == ma_format_u8) {
29629  pConverter->onConvertPCM = ma_pcm_s32_to_u8__avx2;
29630  } else if (pConverter->config.formatOut == ma_format_s16) {
29631  pConverter->onConvertPCM = ma_pcm_s32_to_s16__avx2;
29632  } else if (pConverter->config.formatOut == ma_format_s24) {
29633  pConverter->onConvertPCM = ma_pcm_s32_to_s24__avx2;
29634  } else if (pConverter->config.formatOut == ma_format_s32) {
29635  pConverter->onConvertPCM = ma_pcm_s32_to_s32;
29636  } else if (pConverter->config.formatOut == ma_format_f32) {
29637  pConverter->onConvertPCM = ma_pcm_s32_to_f32__avx2;
29638  }
29639  } break;
29640 
29641  case ma_format_f32:
29642  default:
29643  {
29644  if (pConverter->config.formatOut == ma_format_u8) {
29645  pConverter->onConvertPCM = ma_pcm_f32_to_u8__avx2;
29646  } else if (pConverter->config.formatOut == ma_format_s16) {
29647  pConverter->onConvertPCM = ma_pcm_f32_to_s16__avx2;
29648  } else if (pConverter->config.formatOut == ma_format_s24) {
29649  pConverter->onConvertPCM = ma_pcm_f32_to_s24__avx2;
29650  } else if (pConverter->config.formatOut == ma_format_s32) {
29651  pConverter->onConvertPCM = ma_pcm_f32_to_s32__avx2;
29652  } else if (pConverter->config.formatOut == ma_format_f32) {
29653  pConverter->onConvertPCM = ma_pcm_f32_to_f32;
29654  }
29655  } break;
29656  }
29657 }
29658 #endif
29659 
29660 #if defined(MA_SUPPORT_AVX512)
29661 void ma_format_converter_init_callbacks__avx512(ma_format_converter* pConverter)
29662 {
29663  ma_assert(pConverter != NULL);
29664 
29665  switch (pConverter->config.formatIn)
29666  {
29667  case ma_format_u8:
29668  {
29669  if (pConverter->config.formatOut == ma_format_u8) {
29670  pConverter->onConvertPCM = ma_pcm_u8_to_u8;
29671  } else if (pConverter->config.formatOut == ma_format_s16) {
29672  pConverter->onConvertPCM = ma_pcm_u8_to_s16__avx512;
29673  } else if (pConverter->config.formatOut == ma_format_s24) {
29674  pConverter->onConvertPCM = ma_pcm_u8_to_s24__avx512;
29675  } else if (pConverter->config.formatOut == ma_format_s32) {
29676  pConverter->onConvertPCM = ma_pcm_u8_to_s32__avx512;
29677  } else if (pConverter->config.formatOut == ma_format_f32) {
29678  pConverter->onConvertPCM = ma_pcm_u8_to_f32__avx512;
29679  }
29680  } break;
29681 
29682  case ma_format_s16:
29683  {
29684  if (pConverter->config.formatOut == ma_format_u8) {
29685  pConverter->onConvertPCM = ma_pcm_s16_to_u8__avx512;
29686  } else if (pConverter->config.formatOut == ma_format_s16) {
29687  pConverter->onConvertPCM = ma_pcm_s16_to_s16;
29688  } else if (pConverter->config.formatOut == ma_format_s24) {
29689  pConverter->onConvertPCM = ma_pcm_s16_to_s24__avx512;
29690  } else if (pConverter->config.formatOut == ma_format_s32) {
29691  pConverter->onConvertPCM = ma_pcm_s16_to_s32__avx512;
29692  } else if (pConverter->config.formatOut == ma_format_f32) {
29693  pConverter->onConvertPCM = ma_pcm_s16_to_f32__avx512;
29694  }
29695  } break;
29696 
29697  case ma_format_s24:
29698  {
29699  if (pConverter->config.formatOut == ma_format_u8) {
29700  pConverter->onConvertPCM = ma_pcm_s24_to_u8__avx512;
29701  } else if (pConverter->config.formatOut == ma_format_s16) {
29702  pConverter->onConvertPCM = ma_pcm_s24_to_s16__avx512;
29703  } else if (pConverter->config.formatOut == ma_format_s24) {
29704  pConverter->onConvertPCM = ma_pcm_s24_to_s24;
29705  } else if (pConverter->config.formatOut == ma_format_s32) {
29706  pConverter->onConvertPCM = ma_pcm_s24_to_s32__avx512;
29707  } else if (pConverter->config.formatOut == ma_format_f32) {
29708  pConverter->onConvertPCM = ma_pcm_s24_to_f32__avx512;
29709  }
29710  } break;
29711 
29712  case ma_format_s32:
29713  {
29714  if (pConverter->config.formatOut == ma_format_u8) {
29715  pConverter->onConvertPCM = ma_pcm_s32_to_u8__avx512;
29716  } else if (pConverter->config.formatOut == ma_format_s16) {
29717  pConverter->onConvertPCM = ma_pcm_s32_to_s16__avx512;
29718  } else if (pConverter->config.formatOut == ma_format_s24) {
29719  pConverter->onConvertPCM = ma_pcm_s32_to_s24__avx512;
29720  } else if (pConverter->config.formatOut == ma_format_s32) {
29721  pConverter->onConvertPCM = ma_pcm_s32_to_s32;
29722  } else if (pConverter->config.formatOut == ma_format_f32) {
29723  pConverter->onConvertPCM = ma_pcm_s32_to_f32__avx512;
29724  }
29725  } break;
29726 
29727  case ma_format_f32:
29728  default:
29729  {
29730  if (pConverter->config.formatOut == ma_format_u8) {
29731  pConverter->onConvertPCM = ma_pcm_f32_to_u8__avx512;
29732  } else if (pConverter->config.formatOut == ma_format_s16) {
29733  pConverter->onConvertPCM = ma_pcm_f32_to_s16__avx512;
29734  } else if (pConverter->config.formatOut == ma_format_s24) {
29735  pConverter->onConvertPCM = ma_pcm_f32_to_s24__avx512;
29736  } else if (pConverter->config.formatOut == ma_format_s32) {
29737  pConverter->onConvertPCM = ma_pcm_f32_to_s32__avx512;
29738  } else if (pConverter->config.formatOut == ma_format_f32) {
29739  pConverter->onConvertPCM = ma_pcm_f32_to_f32;
29740  }
29741  } break;
29742  }
29743 }
29744 #endif
29745 
29746 #if defined(MA_SUPPORT_NEON)
29747 void ma_format_converter_init_callbacks__neon(ma_format_converter* pConverter)
29748 {
29749  ma_assert(pConverter != NULL);
29750 
29751  switch (pConverter->config.formatIn)
29752  {
29753  case ma_format_u8:
29754  {
29755  if (pConverter->config.formatOut == ma_format_u8) {
29756  pConverter->onConvertPCM = ma_pcm_u8_to_u8;
29757  } else if (pConverter->config.formatOut == ma_format_s16) {
29758  pConverter->onConvertPCM = ma_pcm_u8_to_s16__neon;
29759  } else if (pConverter->config.formatOut == ma_format_s24) {
29760  pConverter->onConvertPCM = ma_pcm_u8_to_s24__neon;
29761  } else if (pConverter->config.formatOut == ma_format_s32) {
29762  pConverter->onConvertPCM = ma_pcm_u8_to_s32__neon;
29763  } else if (pConverter->config.formatOut == ma_format_f32) {
29764  pConverter->onConvertPCM = ma_pcm_u8_to_f32__neon;
29765  }
29766  } break;
29767 
29768  case ma_format_s16:
29769  {
29770  if (pConverter->config.formatOut == ma_format_u8) {
29771  pConverter->onConvertPCM = ma_pcm_s16_to_u8__neon;
29772  } else if (pConverter->config.formatOut == ma_format_s16) {
29773  pConverter->onConvertPCM = ma_pcm_s16_to_s16;
29774  } else if (pConverter->config.formatOut == ma_format_s24) {
29775  pConverter->onConvertPCM = ma_pcm_s16_to_s24__neon;
29776  } else if (pConverter->config.formatOut == ma_format_s32) {
29777  pConverter->onConvertPCM = ma_pcm_s16_to_s32__neon;
29778  } else if (pConverter->config.formatOut == ma_format_f32) {
29779  pConverter->onConvertPCM = ma_pcm_s16_to_f32__neon;
29780  }
29781  } break;
29782 
29783  case ma_format_s24:
29784  {
29785  if (pConverter->config.formatOut == ma_format_u8) {
29786  pConverter->onConvertPCM = ma_pcm_s24_to_u8__neon;
29787  } else if (pConverter->config.formatOut == ma_format_s16) {
29788  pConverter->onConvertPCM = ma_pcm_s24_to_s16__neon;
29789  } else if (pConverter->config.formatOut == ma_format_s24) {
29790  pConverter->onConvertPCM = ma_pcm_s24_to_s24;
29791  } else if (pConverter->config.formatOut == ma_format_s32) {
29792  pConverter->onConvertPCM = ma_pcm_s24_to_s32__neon;
29793  } else if (pConverter->config.formatOut == ma_format_f32) {
29794  pConverter->onConvertPCM = ma_pcm_s24_to_f32__neon;
29795  }
29796  } break;
29797 
29798  case ma_format_s32:
29799  {
29800  if (pConverter->config.formatOut == ma_format_u8) {
29801  pConverter->onConvertPCM = ma_pcm_s32_to_u8__neon;
29802  } else if (pConverter->config.formatOut == ma_format_s16) {
29803  pConverter->onConvertPCM = ma_pcm_s32_to_s16__neon;
29804  } else if (pConverter->config.formatOut == ma_format_s24) {
29805  pConverter->onConvertPCM = ma_pcm_s32_to_s24__neon;
29806  } else if (pConverter->config.formatOut == ma_format_s32) {
29807  pConverter->onConvertPCM = ma_pcm_s32_to_s32;
29808  } else if (pConverter->config.formatOut == ma_format_f32) {
29809  pConverter->onConvertPCM = ma_pcm_s32_to_f32__neon;
29810  }
29811  } break;
29812 
29813  case ma_format_f32:
29814  default:
29815  {
29816  if (pConverter->config.formatOut == ma_format_u8) {
29817  pConverter->onConvertPCM = ma_pcm_f32_to_u8__neon;
29818  } else if (pConverter->config.formatOut == ma_format_s16) {
29819  pConverter->onConvertPCM = ma_pcm_f32_to_s16__neon;
29820  } else if (pConverter->config.formatOut == ma_format_s24) {
29821  pConverter->onConvertPCM = ma_pcm_f32_to_s24__neon;
29822  } else if (pConverter->config.formatOut == ma_format_s32) {
29823  pConverter->onConvertPCM = ma_pcm_f32_to_s32__neon;
29824  } else if (pConverter->config.formatOut == ma_format_f32) {
29825  pConverter->onConvertPCM = ma_pcm_f32_to_f32;
29826  }
29827  } break;
29828  }
29829 }
29830 #endif
29831 
29833 {
29834  if (pConverter == NULL) {
29835  return MA_INVALID_ARGS;
29836  }
29837  ma_zero_object(pConverter);
29838 
29839  if (pConfig == NULL) {
29840  return MA_INVALID_ARGS;
29841  }
29842 
29843  pConverter->config = *pConfig;
29844 
29845  /* SIMD */
29846  pConverter->useSSE2 = ma_has_sse2() && !pConfig->noSSE2;
29847  pConverter->useAVX2 = ma_has_avx2() && !pConfig->noAVX2;
29848  pConverter->useAVX512 = ma_has_avx512f() && !pConfig->noAVX512;
29849  pConverter->useNEON = ma_has_neon() && !pConfig->noNEON;
29850 
29851 #if defined(MA_SUPPORT_AVX512)
29852  if (pConverter->useAVX512) {
29853  ma_format_converter_init_callbacks__avx512(pConverter);
29854  } else
29855 #endif
29856 #if defined(MA_SUPPORT_AVX2)
29857  if (pConverter->useAVX2) {
29858  ma_format_converter_init_callbacks__avx2(pConverter);
29859  } else
29860 #endif
29861 #if defined(MA_SUPPORT_SSE2)
29862  if (pConverter->useSSE2) {
29863  ma_format_converter_init_callbacks__sse2(pConverter);
29864  } else
29865 #endif
29866 #if defined(MA_SUPPORT_NEON)
29867  if (pConverter->useNEON) {
29868  ma_format_converter_init_callbacks__neon(pConverter);
29869  } else
29870 #endif
29871  {
29872  ma_format_converter_init_callbacks__default(pConverter);
29873  }
29874 
29875  switch (pConfig->formatOut)
29876  {
29877  case ma_format_u8:
29878  {
29879  pConverter->onInterleavePCM = ma_pcm_interleave_u8;
29880  pConverter->onDeinterleavePCM = ma_pcm_deinterleave_u8;
29881  } break;
29882  case ma_format_s16:
29883  {
29884  pConverter->onInterleavePCM = ma_pcm_interleave_s16;
29885  pConverter->onDeinterleavePCM = ma_pcm_deinterleave_s16;
29886  } break;
29887  case ma_format_s24:
29888  {
29889  pConverter->onInterleavePCM = ma_pcm_interleave_s24;
29890  pConverter->onDeinterleavePCM = ma_pcm_deinterleave_s24;
29891  } break;
29892  case ma_format_s32:
29893  {
29894  pConverter->onInterleavePCM = ma_pcm_interleave_s32;
29895  pConverter->onDeinterleavePCM = ma_pcm_deinterleave_s32;
29896  } break;
29897  case ma_format_f32:
29898  default:
29899  {
29900  pConverter->onInterleavePCM = ma_pcm_interleave_f32;
29901  pConverter->onDeinterleavePCM = ma_pcm_deinterleave_f32;
29902  } break;
29903  }
29904 
29905  return MA_SUCCESS;
29906 }
29907 
29908 ma_uint64 ma_format_converter_read(ma_format_converter* pConverter, ma_uint64 frameCount, void* pFramesOut, void* pUserData)
29909 {
29910  ma_uint64 totalFramesRead;
29911  ma_uint32 sampleSizeIn;
29912  ma_uint32 sampleSizeOut;
29913  ma_uint32 frameSizeOut;
29914  ma_uint8* pNextFramesOut;
29915 
29916  if (pConverter == NULL || pFramesOut == NULL) {
29917  return 0;
29918  }
29919 
29920  totalFramesRead = 0;
29921  sampleSizeIn = ma_get_bytes_per_sample(pConverter->config.formatIn);
29922  sampleSizeOut = ma_get_bytes_per_sample(pConverter->config.formatOut);
29923  /*frameSizeIn = sampleSizeIn * pConverter->config.channels;*/
29924  frameSizeOut = sampleSizeOut * pConverter->config.channels;
29925  pNextFramesOut = (ma_uint8*)pFramesOut;
29926 
29927  if (pConverter->config.onRead != NULL) {
29928  /* Input data is interleaved. */
29929  if (pConverter->config.formatIn == pConverter->config.formatOut) {
29930  /* Pass through. */
29931  while (totalFramesRead < frameCount) {
29932  ma_uint32 framesJustRead;
29933  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
29934  ma_uint64 framesToReadRightNow = framesRemaining;
29935  if (framesToReadRightNow > 0xFFFFFFFF) {
29936  framesToReadRightNow = 0xFFFFFFFF;
29937  }
29938 
29939  framesJustRead = (ma_uint32)pConverter->config.onRead(pConverter, (ma_uint32)framesToReadRightNow, pNextFramesOut, pUserData);
29940  if (framesJustRead == 0) {
29941  break;
29942  }
29943 
29944  totalFramesRead += framesJustRead;
29945  pNextFramesOut += framesJustRead * frameSizeOut;
29946 
29947  if (framesJustRead < framesToReadRightNow) {
29948  break;
29949  }
29950  }
29951  } else {
29952  /* Conversion required. */
29953  ma_uint32 maxFramesToReadAtATime;
29954 
29956  ma_assert(sizeof(temp) <= 0xFFFFFFFF);
29957 
29958  maxFramesToReadAtATime = sizeof(temp) / sampleSizeIn / pConverter->config.channels;
29959 
29960  while (totalFramesRead < frameCount) {
29961  ma_uint32 framesJustRead;
29962  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
29963  ma_uint64 framesToReadRightNow = framesRemaining;
29964  if (framesToReadRightNow > maxFramesToReadAtATime) {
29965  framesToReadRightNow = maxFramesToReadAtATime;
29966  }
29967 
29968  framesJustRead = (ma_uint32)pConverter->config.onRead(pConverter, (ma_uint32)framesToReadRightNow, temp, pUserData);
29969  if (framesJustRead == 0) {
29970  break;
29971  }
29972 
29973  pConverter->onConvertPCM(pNextFramesOut, temp, framesJustRead*pConverter->config.channels, pConverter->config.ditherMode);
29974 
29975  totalFramesRead += framesJustRead;
29976  pNextFramesOut += framesJustRead * frameSizeOut;
29977 
29978  if (framesJustRead < framesToReadRightNow) {
29979  break;
29980  }
29981  }
29982  }
29983  } else {
29984  /* Input data is deinterleaved. If a conversion is required we need to do an intermediary step. */
29985  void* ppTempSamplesOfOutFormat[MA_MAX_CHANNELS];
29986  size_t splitBufferSizeOut;
29987  ma_uint32 maxFramesToReadAtATime;
29988 
29990  ma_assert(sizeof(tempSamplesOfOutFormat) <= 0xFFFFFFFF);
29991 
29992  ma_split_buffer(tempSamplesOfOutFormat, sizeof(tempSamplesOfOutFormat), pConverter->config.channels, MA_SIMD_ALIGNMENT, (void**)&ppTempSamplesOfOutFormat, &splitBufferSizeOut);
29993 
29994  maxFramesToReadAtATime = (ma_uint32)(splitBufferSizeOut / sampleSizeIn);
29995 
29996  while (totalFramesRead < frameCount) {
29997  ma_uint32 framesJustRead;
29998  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
29999  ma_uint64 framesToReadRightNow = framesRemaining;
30000  if (framesToReadRightNow > maxFramesToReadAtATime) {
30001  framesToReadRightNow = maxFramesToReadAtATime;
30002  }
30003 
30004  if (pConverter->config.formatIn == pConverter->config.formatOut) {
30005  /* Only interleaving. */
30006  framesJustRead = (ma_uint32)pConverter->config.onReadDeinterleaved(pConverter, (ma_uint32)framesToReadRightNow, ppTempSamplesOfOutFormat, pUserData);
30007  if (framesJustRead == 0) {
30008  break;
30009  }
30010  } else {
30011  /* Interleaving + Conversion. Convert first, then interleave. */
30012  void* ppTempSamplesOfInFormat[MA_MAX_CHANNELS];
30013  size_t splitBufferSizeIn;
30014  ma_uint32 iChannel;
30015 
30017 
30018  ma_split_buffer(tempSamplesOfInFormat, sizeof(tempSamplesOfInFormat), pConverter->config.channels, MA_SIMD_ALIGNMENT, (void**)&ppTempSamplesOfInFormat, &splitBufferSizeIn);
30019 
30020  if (framesToReadRightNow > (splitBufferSizeIn / sampleSizeIn)) {
30021  framesToReadRightNow = (splitBufferSizeIn / sampleSizeIn);
30022  }
30023 
30024  framesJustRead = (ma_uint32)pConverter->config.onReadDeinterleaved(pConverter, (ma_uint32)framesToReadRightNow, ppTempSamplesOfInFormat, pUserData);
30025  if (framesJustRead == 0) {
30026  break;
30027  }
30028 
30029  for (iChannel = 0; iChannel < pConverter->config.channels; iChannel += 1) {
30030  pConverter->onConvertPCM(ppTempSamplesOfOutFormat[iChannel], ppTempSamplesOfInFormat[iChannel], framesJustRead, pConverter->config.ditherMode);
30031  }
30032  }
30033 
30034  pConverter->onInterleavePCM(pNextFramesOut, (const void**)ppTempSamplesOfOutFormat, framesJustRead, pConverter->config.channels);
30035 
30036  totalFramesRead += framesJustRead;
30037  pNextFramesOut += framesJustRead * frameSizeOut;
30038 
30039  if (framesJustRead < framesToReadRightNow) {
30040  break;
30041  }
30042  }
30043  }
30044 
30045  return totalFramesRead;
30046 }
30047 
30048 ma_uint64 ma_format_converter_read_deinterleaved(ma_format_converter* pConverter, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData)
30049 {
30050  ma_uint64 totalFramesRead;
30051  ma_uint32 sampleSizeIn;
30052  ma_uint32 sampleSizeOut;
30053  ma_uint8* ppNextSamplesOut[MA_MAX_CHANNELS];
30054 
30055  if (pConverter == NULL || ppSamplesOut == NULL) {
30056  return 0;
30057  }
30058 
30059  totalFramesRead = 0;
30060  sampleSizeIn = ma_get_bytes_per_sample(pConverter->config.formatIn);
30061  sampleSizeOut = ma_get_bytes_per_sample(pConverter->config.formatOut);
30062 
30063  ma_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(void*) * pConverter->config.channels);
30064 
30065  if (pConverter->config.onRead != NULL) {
30066  /* Input data is interleaved. */
30067  ma_uint32 maxFramesToReadAtATime;
30068 
30070  ma_assert(sizeof(tempSamplesOfOutFormat) <= 0xFFFFFFFF);
30071 
30072  maxFramesToReadAtATime = sizeof(tempSamplesOfOutFormat) / sampleSizeIn / pConverter->config.channels;
30073 
30074  while (totalFramesRead < frameCount) {
30075  ma_uint32 iChannel;
30076  ma_uint32 framesJustRead;
30077  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
30078  ma_uint64 framesToReadRightNow = framesRemaining;
30079  if (framesToReadRightNow > maxFramesToReadAtATime) {
30080  framesToReadRightNow = maxFramesToReadAtATime;
30081  }
30082 
30083  if (pConverter->config.formatIn == pConverter->config.formatOut) {
30084  /* Only de-interleaving. */
30085  framesJustRead = (ma_uint32)pConverter->config.onRead(pConverter, (ma_uint32)framesToReadRightNow, tempSamplesOfOutFormat, pUserData);
30086  if (framesJustRead == 0) {
30087  break;
30088  }
30089  } else {
30090  /* De-interleaving + Conversion. Convert first, then de-interleave. */
30091  MA_ALIGN(MA_SIMD_ALIGNMENT) ma_uint8 tempSamplesOfInFormat[sizeof(tempSamplesOfOutFormat)];
30092 
30093  framesJustRead = (ma_uint32)pConverter->config.onRead(pConverter, (ma_uint32)framesToReadRightNow, tempSamplesOfInFormat, pUserData);
30094  if (framesJustRead == 0) {
30095  break;
30096  }
30097 
30098  pConverter->onConvertPCM(tempSamplesOfOutFormat, tempSamplesOfInFormat, framesJustRead * pConverter->config.channels, pConverter->config.ditherMode);
30099  }
30100 
30101  pConverter->onDeinterleavePCM((void**)ppNextSamplesOut, tempSamplesOfOutFormat, framesJustRead, pConverter->config.channels);
30102 
30103  totalFramesRead += framesJustRead;
30104  for (iChannel = 0; iChannel < pConverter->config.channels; ++iChannel) {
30105  ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeOut;
30106  }
30107 
30108  if (framesJustRead < framesToReadRightNow) {
30109  break;
30110  }
30111  }
30112  } else {
30113  /* Input data is deinterleaved. */
30114  if (pConverter->config.formatIn == pConverter->config.formatOut) {
30115  /* Pass through. */
30116  while (totalFramesRead < frameCount) {
30117  ma_uint32 iChannel;
30118  ma_uint32 framesJustRead;
30119  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
30120  ma_uint64 framesToReadRightNow = framesRemaining;
30121  if (framesToReadRightNow > 0xFFFFFFFF) {
30122  framesToReadRightNow = 0xFFFFFFFF;
30123  }
30124 
30125  framesJustRead = (ma_uint32)pConverter->config.onReadDeinterleaved(pConverter, (ma_uint32)framesToReadRightNow, (void**)ppNextSamplesOut, pUserData);
30126  if (framesJustRead == 0) {
30127  break;
30128  }
30129 
30130  totalFramesRead += framesJustRead;
30131  for (iChannel = 0; iChannel < pConverter->config.channels; ++iChannel) {
30132  ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeOut;
30133  }
30134 
30135  if (framesJustRead < framesToReadRightNow) {
30136  break;
30137  }
30138  }
30139  } else {
30140  /* Conversion required. */
30141  void* ppTemp[MA_MAX_CHANNELS];
30142  size_t splitBufferSize;
30143  ma_uint32 maxFramesToReadAtATime;
30144 
30146  ma_assert(sizeof(temp) <= 0xFFFFFFFF);
30147 
30148  ma_split_buffer(temp, sizeof(temp), pConverter->config.channels, MA_SIMD_ALIGNMENT, (void**)&ppTemp, &splitBufferSize);
30149 
30150  maxFramesToReadAtATime = (ma_uint32)(splitBufferSize / sampleSizeIn);
30151 
30152  while (totalFramesRead < frameCount) {
30153  ma_uint32 iChannel;
30154  ma_uint32 framesJustRead;
30155  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
30156  ma_uint64 framesToReadRightNow = framesRemaining;
30157  if (framesToReadRightNow > maxFramesToReadAtATime) {
30158  framesToReadRightNow = maxFramesToReadAtATime;
30159  }
30160 
30161  framesJustRead = (ma_uint32)pConverter->config.onReadDeinterleaved(pConverter, (ma_uint32)framesToReadRightNow, ppTemp, pUserData);
30162  if (framesJustRead == 0) {
30163  break;
30164  }
30165 
30166  for (iChannel = 0; iChannel < pConverter->config.channels; iChannel += 1) {
30167  pConverter->onConvertPCM(ppNextSamplesOut[iChannel], ppTemp[iChannel], framesJustRead, pConverter->config.ditherMode);
30168  ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeOut;
30169  }
30170 
30171  totalFramesRead += framesJustRead;
30172 
30173  if (framesJustRead < framesToReadRightNow) {
30174  break;
30175  }
30176  }
30177  }
30178  }
30179 
30180  return totalFramesRead;
30181 }
30182 
30183 
30185 {
30187  ma_zero_object(&config);
30188 
30189  return config;
30190 }
30191 
30193 {
30195  config.formatIn = formatIn;
30196  config.formatOut = formatOut;
30197  config.channels = channels;
30198  config.onRead = onRead;
30199  config.onReadDeinterleaved = NULL;
30200  config.pUserData = pUserData;
30201 
30202  return config;
30203 }
30204 
30206 {
30207  ma_format_converter_config config = ma_format_converter_config_init(formatIn, formatOut, channels, NULL, pUserData);
30208  config.onReadDeinterleaved = onReadDeinterleaved;
30209 
30210  return config;
30211 }
30212 
30213 
30214 
30215 /**************************************************************************************************************************************************************
30216 
30217 Channel Routing
30218 
30219 **************************************************************************************************************************************************************/
30220 
30221 /*
30222 -X = Left, +X = Right
30223 -Y = Bottom, +Y = Top
30224 -Z = Front, +Z = Back
30225 */
30226 typedef struct
30227 {
30228  float x;
30229  float y;
30230  float z;
30231 } ma_vec3;
30232 
30233 static MA_INLINE ma_vec3 ma_vec3f(float x, float y, float z)
30234 {
30235  ma_vec3 r;
30236  r.x = x;
30237  r.y = y;
30238  r.z = z;
30239 
30240  return r;
30241 }
30242 
30243 static MA_INLINE ma_vec3 ma_vec3_add(ma_vec3 a, ma_vec3 b)
30244 {
30245  return ma_vec3f(
30246  a.x + b.x,
30247  a.y + b.y,
30248  a.z + b.z
30249  );
30250 }
30251 
30252 static MA_INLINE ma_vec3 ma_vec3_sub(ma_vec3 a, ma_vec3 b)
30253 {
30254  return ma_vec3f(
30255  a.x - b.x,
30256  a.y - b.y,
30257  a.z - b.z
30258  );
30259 }
30260 
30261 static MA_INLINE ma_vec3 ma_vec3_mul(ma_vec3 a, ma_vec3 b)
30262 {
30263  return ma_vec3f(
30264  a.x * b.x,
30265  a.y * b.y,
30266  a.z * b.z
30267  );
30268 }
30269 
30270 static MA_INLINE ma_vec3 ma_vec3_div(ma_vec3 a, ma_vec3 b)
30271 {
30272  return ma_vec3f(
30273  a.x / b.x,
30274  a.y / b.y,
30275  a.z / b.z
30276  );
30277 }
30278 
30279 static MA_INLINE float ma_vec3_dot(ma_vec3 a, ma_vec3 b)
30280 {
30281  return a.x*b.x + a.y*b.y + a.z*b.z;
30282 }
30283 
30284 static MA_INLINE float ma_vec3_length2(ma_vec3 a)
30285 {
30286  return ma_vec3_dot(a, a);
30287 }
30288 
30289 static MA_INLINE float ma_vec3_length(ma_vec3 a)
30290 {
30291  return (float)sqrt(ma_vec3_length2(a));
30292 }
30293 
30294 static MA_INLINE ma_vec3 ma_vec3_normalize(ma_vec3 a)
30295 {
30296  float len = 1 / ma_vec3_length(a);
30297 
30298  ma_vec3 r;
30299  r.x = a.x * len;
30300  r.y = a.y * len;
30301  r.z = a.z * len;
30302 
30303  return r;
30304 }
30305 
30306 static MA_INLINE float ma_vec3_distance(ma_vec3 a, ma_vec3 b)
30307 {
30308  return ma_vec3_length(ma_vec3_sub(a, b));
30309 }
30310 
30311 
30312 #define MA_PLANE_LEFT 0
30313 #define MA_PLANE_RIGHT 1
30314 #define MA_PLANE_FRONT 2
30315 #define MA_PLANE_BACK 3
30316 #define MA_PLANE_BOTTOM 4
30317 #define MA_PLANE_TOP 5
30318 
30319 float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = {
30320  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_NONE */
30321  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_MONO */
30322  { 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT */
30323  { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT */
30324  { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_CENTER */
30325  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_LFE */
30326  { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_LEFT */
30327  { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_RIGHT */
30328  { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_LEFT_CENTER */
30329  { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_FRONT_RIGHT_CENTER */
30330  { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, /* MA_CHANNEL_BACK_CENTER */
30331  { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_LEFT */
30332  { 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_SIDE_RIGHT */
30333  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, /* MA_CHANNEL_TOP_CENTER */
30334  { 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_LEFT */
30335  { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_FRONT_CENTER */
30336  { 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_FRONT_RIGHT */
30337  { 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_LEFT */
30338  { 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f}, /* MA_CHANNEL_TOP_BACK_CENTER */
30339  { 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f}, /* MA_CHANNEL_TOP_BACK_RIGHT */
30340  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_0 */
30341  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_1 */
30342  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_2 */
30343  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_3 */
30344  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_4 */
30345  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_5 */
30346  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_6 */
30347  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_7 */
30348  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_8 */
30349  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_9 */
30350  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_10 */
30351  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_11 */
30352  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_12 */
30353  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_13 */
30354  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_14 */
30355  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_15 */
30356  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_16 */
30357  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_17 */
30358  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_18 */
30359  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_19 */
30360  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_20 */
30361  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_21 */
30362  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_22 */
30363  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_23 */
30364  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_24 */
30365  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_25 */
30366  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_26 */
30367  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_27 */
30368  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_28 */
30369  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_29 */
30370  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_30 */
30371  { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */
30372 };
30373 
30374 float ma_calculate_channel_position_planar_weight(ma_channel channelPositionA, ma_channel channelPositionB)
30375 {
30376  /*
30377  Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to
30378  the following output configuration:
30379 
30380  - front/left
30381  - side/left
30382  - back/left
30383 
30384  The front/left output is easy - it the same speaker position so it receives the full contribution of the front/left input. The amount
30385  of contribution to apply to the side/left and back/left speakers, however, is a bit more complicated.
30386 
30387  Imagine the front/left speaker as emitting audio from two planes - the front plane and the left plane. You can think of the front/left
30388  speaker emitting half of it's total volume from the front, and the other half from the left. Since part of it's volume is being emitted
30389  from the left side, and the side/left and back/left channels also emit audio from the left plane, one would expect that they would
30390  receive some amount of contribution from front/left speaker. The amount of contribution depends on how many planes are shared between
30391  the two speakers. Note that in the examples below I've added a top/front/left speaker as an example just to show how the math works
30392  across 3 spatial dimensions.
30393 
30394  The first thing to do is figure out how each speaker's volume is spread over each of plane:
30395  - front/left: 2 planes (front and left) = 1/2 = half it's total volume on each plane
30396  - side/left: 1 plane (left only) = 1/1 = entire volume from left plane
30397  - back/left: 2 planes (back and left) = 1/2 = half it's total volume on each plane
30398  - top/front/left: 3 planes (top, front and left) = 1/3 = one third it's total volume on each plane
30399 
30400  The amount of volume each channel contributes to each of it's planes is what controls how much it is willing to given and take to other
30401  channels on the same plane. The volume that is willing to the given by one channel is multiplied by the volume that is willing to be
30402  taken by the other to produce the final contribution.
30403  */
30404 
30405  /* Contribution = Sum(Volume to Give * Volume to Take) */
30406  float contribution =
30407  g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] +
30408  g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] +
30409  g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] +
30410  g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] +
30411  g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] +
30412  g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5];
30413 
30414  return contribution;
30415 }
30416 
30417 float ma_channel_router__calculate_input_channel_planar_weight(const ma_channel_router* pRouter, ma_channel channelPositionIn, ma_channel channelPositionOut)
30418 {
30419  ma_assert(pRouter != NULL);
30420  (void)pRouter;
30421 
30422  return ma_calculate_channel_position_planar_weight(channelPositionIn, channelPositionOut);
30423 }
30424 
30425 ma_bool32 ma_channel_router__is_spatial_channel_position(const ma_channel_router* pRouter, ma_channel channelPosition)
30426 {
30427  int i;
30428 
30429  ma_assert(pRouter != NULL);
30430  (void)pRouter;
30431 
30432  if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) {
30433  return MA_FALSE;
30434  }
30435 
30436  for (i = 0; i < 6; ++i) {
30437  if (g_maChannelPlaneRatios[channelPosition][i] != 0) {
30438  return MA_TRUE;
30439  }
30440  }
30441 
30442  return MA_FALSE;
30443 }
30444 
30446 {
30447  ma_uint32 iChannelIn;
30448  ma_uint32 iChannelOut;
30449 
30450  if (pRouter == NULL) {
30451  return MA_INVALID_ARGS;
30452  }
30453 
30454  ma_zero_object(pRouter);
30455 
30456  if (pConfig == NULL) {
30457  return MA_INVALID_ARGS;
30458  }
30459  if (pConfig->onReadDeinterleaved == NULL) {
30460  return MA_INVALID_ARGS;
30461  }
30462 
30463  if (!ma_channel_map_valid(pConfig->channelsIn, pConfig->channelMapIn)) {
30464  return MA_INVALID_ARGS; /* Invalid input channel map. */
30465  }
30466  if (!ma_channel_map_valid(pConfig->channelsOut, pConfig->channelMapOut)) {
30467  return MA_INVALID_ARGS; /* Invalid output channel map. */
30468  }
30469 
30470  pRouter->config = *pConfig;
30471 
30472  /* SIMD */
30473  pRouter->useSSE2 = ma_has_sse2() && !pConfig->noSSE2;
30474  pRouter->useAVX2 = ma_has_avx2() && !pConfig->noAVX2;
30475  pRouter->useAVX512 = ma_has_avx512f() && !pConfig->noAVX512;
30476  pRouter->useNEON = ma_has_neon() && !pConfig->noNEON;
30477 
30478  /* If the input and output channels and channel maps are the same we should use a passthrough. */
30479  if (pRouter->config.channelsIn == pRouter->config.channelsOut) {
30480  if (ma_channel_map_equal(pRouter->config.channelsIn, pRouter->config.channelMapIn, pRouter->config.channelMapOut)) {
30481  pRouter->isPassthrough = MA_TRUE;
30482  }
30484  pRouter->isPassthrough = MA_TRUE;
30485  }
30486  }
30487 
30488  /*
30489  We can use a simple case for expanding the mono channel. This will when expanding a mono input into any output so long
30490  as no LFE is present in the output.
30491  */
30492  if (!pRouter->isPassthrough) {
30493  if (pRouter->config.channelsIn == 1 && pRouter->config.channelMapIn[0] == MA_CHANNEL_MONO) {
30494  /* Optimal case if no LFE is in the output channel map. */
30495  pRouter->isSimpleMonoExpansion = MA_TRUE;
30497  pRouter->isSimpleMonoExpansion = MA_FALSE;
30498  }
30499  }
30500  }
30501 
30502  /* Another optimized case is stereo to mono. */
30503  if (!pRouter->isPassthrough) {
30504  if (pRouter->config.channelsOut == 1 && pRouter->config.channelMapOut[0] == MA_CHANNEL_MONO && pRouter->config.channelsIn == 2) {
30505  /* Optimal case if no LFE is in the input channel map. */
30506  pRouter->isStereoToMono = MA_TRUE;
30508  pRouter->isStereoToMono = MA_FALSE;
30509  }
30510  }
30511  }
30512 
30513  /*
30514  Here is where we do a bit of pre-processing to know how each channel should be combined to make up the output. Rules:
30515 
30516  1) If it's a passthrough, do nothing - it's just a simple memcpy().
30517  2) If the channel counts are the same and every channel position in the input map is present in the output map, use a
30518  simple shuffle. An example might be different 5.1 channel layouts.
30519  3) Otherwise channels are blended based on spatial locality.
30520  */
30521  if (!pRouter->isPassthrough) {
30522  if (pRouter->config.channelsIn == pRouter->config.channelsOut) {
30523  ma_bool32 areAllChannelPositionsPresent = MA_TRUE;
30524  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
30525  ma_bool32 isInputChannelPositionInOutput = MA_FALSE;
30526  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
30527  if (pRouter->config.channelMapIn[iChannelIn] == pRouter->config.channelMapOut[iChannelOut]) {
30528  isInputChannelPositionInOutput = MA_TRUE;
30529  break;
30530  }
30531  }
30532 
30533  if (!isInputChannelPositionInOutput) {
30534  areAllChannelPositionsPresent = MA_FALSE;
30535  break;
30536  }
30537  }
30538 
30539  if (areAllChannelPositionsPresent) {
30540  pRouter->isSimpleShuffle = MA_TRUE;
30541 
30542  /*
30543  All the router will be doing is rearranging channels which means all we need to do is use a shuffling table which is just
30544  a mapping between the index of the input channel to the index of the output channel.
30545  */
30546  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
30547  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
30548  if (pRouter->config.channelMapIn[iChannelIn] == pRouter->config.channelMapOut[iChannelOut]) {
30549  pRouter->shuffleTable[iChannelIn] = (ma_uint8)iChannelOut;
30550  break;
30551  }
30552  }
30553  }
30554  }
30555  }
30556  }
30557 
30558 
30559  /*
30560  Here is where weights are calculated. Note that we calculate the weights at all times, even when using a passthrough and simple
30561  shuffling. We use different algorithms for calculating weights depending on our mixing mode.
30562 
30563  In simple mode we don't do any blending (except for converting between mono, which is done in a later step). Instead we just
30564  map 1:1 matching channels. In this mode, if no channels in the input channel map correspond to anything in the output channel
30565  map, nothing will be heard!
30566  */
30567 
30568  /* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */
30569  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
30570  ma_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
30571 
30572  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
30573  ma_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
30574 
30575  if (channelPosIn == channelPosOut) {
30576  pRouter->config.weights[iChannelIn][iChannelOut] = 1;
30577  }
30578  }
30579  }
30580 
30581  /*
30582  The mono channel is accumulated on all other channels, except LFE. Make sure in this loop we exclude output mono channels since
30583  they were handled in the pass above.
30584  */
30585  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
30586  ma_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
30587 
30588  if (channelPosIn == MA_CHANNEL_MONO) {
30589  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
30590  ma_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
30591 
30592  if (channelPosOut != MA_CHANNEL_NONE && channelPosOut != MA_CHANNEL_MONO && channelPosOut != MA_CHANNEL_LFE) {
30593  pRouter->config.weights[iChannelIn][iChannelOut] = 1;
30594  }
30595  }
30596  }
30597  }
30598 
30599  /* The output mono channel is the average of all non-none, non-mono and non-lfe input channels. */
30600  {
30601  ma_uint32 len = 0;
30602  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
30603  ma_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
30604 
30605  if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) {
30606  len += 1;
30607  }
30608  }
30609 
30610  if (len > 0) {
30611  float monoWeight = 1.0f / len;
30612 
30613  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
30614  ma_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
30615 
30616  if (channelPosOut == MA_CHANNEL_MONO) {
30617  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
30618  ma_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
30619 
30620  if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) {
30621  pRouter->config.weights[iChannelIn][iChannelOut] += monoWeight;
30622  }
30623  }
30624  }
30625  }
30626  }
30627  }
30628 
30629 
30630  /* Input and output channels that are not present on the other side need to be blended in based on spatial locality. */
30631  switch (pRouter->config.mixingMode)
30632  {
30634  {
30635  /* Unmapped input channels. */
30636  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
30637  ma_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
30638 
30639  if (ma_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) {
30640  if (!ma_channel_map_contains_channel_position(pRouter->config.channelsOut, pRouter->config.channelMapOut, channelPosIn)) {
30641  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
30642  ma_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
30643 
30644  if (ma_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
30645  float weight = 0;
30647  weight = ma_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut);
30648  }
30649 
30650  /* Only apply the weight if we haven't already got some contribution from the respective channels. */
30651  if (pRouter->config.weights[iChannelIn][iChannelOut] == 0) {
30652  pRouter->config.weights[iChannelIn][iChannelOut] = weight;
30653  }
30654  }
30655  }
30656  }
30657  }
30658  }
30659 
30660  /* Unmapped output channels. */
30661  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
30662  ma_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
30663 
30664  if (ma_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
30665  if (!ma_channel_map_contains_channel_position(pRouter->config.channelsIn, pRouter->config.channelMapIn, channelPosOut)) {
30666  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
30667  ma_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
30668 
30669  if (ma_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) {
30670  float weight = 0;
30672  weight = ma_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut);
30673  }
30674 
30675  /* Only apply the weight if we haven't already got some contribution from the respective channels. */
30676  if (pRouter->config.weights[iChannelIn][iChannelOut] == 0) {
30677  pRouter->config.weights[iChannelIn][iChannelOut] = weight;
30678  }
30679  }
30680  }
30681  }
30682  }
30683  }
30684  } break;
30685 
30688  default:
30689  {
30690  /* Fallthrough. */
30691  } break;
30692  }
30693 
30694  return MA_SUCCESS;
30695 }
30696 
30697 static MA_INLINE ma_bool32 ma_channel_router__can_use_sse2(ma_channel_router* pRouter, const float* pSamplesOut, const float* pSamplesIn)
30698 {
30699  return pRouter->useSSE2 && (((ma_uintptr)pSamplesOut & 15) == 0) && (((ma_uintptr)pSamplesIn & 15) == 0);
30700 }
30701 
30702 static MA_INLINE ma_bool32 ma_channel_router__can_use_avx2(ma_channel_router* pRouter, const float* pSamplesOut, const float* pSamplesIn)
30703 {
30704  return pRouter->useAVX2 && (((ma_uintptr)pSamplesOut & 31) == 0) && (((ma_uintptr)pSamplesIn & 31) == 0);
30705 }
30706 
30707 static MA_INLINE ma_bool32 ma_channel_router__can_use_avx512(ma_channel_router* pRouter, const float* pSamplesOut, const float* pSamplesIn)
30708 {
30709  return pRouter->useAVX512 && (((ma_uintptr)pSamplesOut & 63) == 0) && (((ma_uintptr)pSamplesIn & 63) == 0);
30710 }
30711 
30712 static MA_INLINE ma_bool32 ma_channel_router__can_use_neon(ma_channel_router* pRouter, const float* pSamplesOut, const float* pSamplesIn)
30713 {
30714  return pRouter->useNEON && (((ma_uintptr)pSamplesOut & 15) == 0) && (((ma_uintptr)pSamplesIn & 15) == 0);
30715 }
30716 
30717 void ma_channel_router__do_routing(ma_channel_router* pRouter, ma_uint64 frameCount, float** ppSamplesOut, const float** ppSamplesIn)
30718 {
30719  ma_uint32 iChannelIn;
30720  ma_uint32 iChannelOut;
30721 
30722  ma_assert(pRouter != NULL);
30723  ma_assert(pRouter->isPassthrough == MA_FALSE);
30724 
30725  if (pRouter->isSimpleShuffle) {
30726  /* A shuffle is just a re-arrangement of channels and does not require any arithmetic. */
30727  ma_assert(pRouter->config.channelsIn == pRouter->config.channelsOut);
30728  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
30729  iChannelOut = pRouter->shuffleTable[iChannelIn];
30730  ma_copy_memory_64(ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn], frameCount * sizeof(float));
30731  }
30732  } else if (pRouter->isSimpleMonoExpansion) {
30733  /* Simple case for expanding from mono. */
30734  if (pRouter->config.channelsOut == 2) {
30735  ma_uint64 iFrame;
30736  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
30737  ppSamplesOut[0][iFrame] = ppSamplesIn[0][iFrame];
30738  ppSamplesOut[1][iFrame] = ppSamplesIn[0][iFrame];
30739  }
30740  } else {
30741  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
30742  ma_uint64 iFrame;
30743  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
30744  ppSamplesOut[iChannelOut][iFrame] = ppSamplesIn[0][iFrame];
30745  }
30746  }
30747  }
30748  } else if (pRouter->isStereoToMono) {
30749  ma_uint64 iFrame;
30750 
30751  /* Simple case for going from stereo to mono. */
30752  ma_assert(pRouter->config.channelsIn == 2);
30753  ma_assert(pRouter->config.channelsOut == 1);
30754 
30755  for (iFrame = 0; iFrame < frameCount; ++iFrame) {
30756  ppSamplesOut[0][iFrame] = (ppSamplesIn[0][iFrame] + ppSamplesIn[1][iFrame]) * 0.5f;
30757  }
30758  } else {
30759  /* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */
30760 
30761  /* Clear. */
30762  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
30763  ma_zero_memory_64(ppSamplesOut[iChannelOut], frameCount * sizeof(float));
30764  }
30765 
30766  /* Accumulate. */
30767  for (iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
30768  for (iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
30769  ma_uint64 iFrame = 0;
30770 #if defined(MA_SUPPORT_NEON)
30771  if (ma_channel_router__can_use_neon(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
30772  float32x4_t weight = vmovq_n_f32(pRouter->config.weights[iChannelIn][iChannelOut]);
30773  ma_uint64 frameCount4 = frameCount/4;
30774  ma_uint64 iFrame4;
30775 
30776  for (iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
30777  float32x4_t* pO = (float32x4_t*)ppSamplesOut[iChannelOut] + iFrame4;
30778  float32x4_t* pI = (float32x4_t*)ppSamplesIn [iChannelIn ] + iFrame4;
30779  *pO = vaddq_f32(*pO, vmulq_f32(*pI, weight));
30780  }
30781 
30782  iFrame += frameCount4*4;
30783  }
30784  else
30785 #endif
30786 #if defined(MA_SUPPORT_AVX512)
30787  if (ma_channel_router__can_use_avx512(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
30788  __m512 weight = _mm512_set1_ps(pRouter->config.weights[iChannelIn][iChannelOut]);
30789  ma_uint64 frameCount16 = frameCount/16;
30790  ma_uint64 iFrame16;
30791 
30792  for (iFrame16 = 0; iFrame16 < frameCount16; iFrame16 += 1) {
30793  __m512* pO = (__m512*)ppSamplesOut[iChannelOut] + iFrame16;
30794  __m512* pI = (__m512*)ppSamplesIn [iChannelIn ] + iFrame16;
30795  *pO = _mm512_add_ps(*pO, _mm512_mul_ps(*pI, weight));
30796  }
30797 
30798  iFrame += frameCount16*16;
30799  }
30800  else
30801 #endif
30802 #if defined(MA_SUPPORT_AVX2)
30803  if (ma_channel_router__can_use_avx2(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
30804  __m256 weight = _mm256_set1_ps(pRouter->config.weights[iChannelIn][iChannelOut]);
30805  ma_uint64 frameCount8 = frameCount/8;
30806  ma_uint64 iFrame8;
30807 
30808  for (iFrame8 = 0; iFrame8 < frameCount8; iFrame8 += 1) {
30809  __m256* pO = (__m256*)ppSamplesOut[iChannelOut] + iFrame8;
30810  __m256* pI = (__m256*)ppSamplesIn [iChannelIn ] + iFrame8;
30811  *pO = _mm256_add_ps(*pO, _mm256_mul_ps(*pI, weight));
30812  }
30813 
30814  iFrame += frameCount8*8;
30815  }
30816  else
30817 #endif
30818 #if defined(MA_SUPPORT_SSE2)
30819  if (ma_channel_router__can_use_sse2(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
30820  __m128 weight = _mm_set1_ps(pRouter->config.weights[iChannelIn][iChannelOut]);
30821  ma_uint64 frameCount4 = frameCount/4;
30822  ma_uint64 iFrame4;
30823 
30824  for (iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
30825  __m128* pO = (__m128*)ppSamplesOut[iChannelOut] + iFrame4;
30826  __m128* pI = (__m128*)ppSamplesIn [iChannelIn ] + iFrame4;
30827  *pO = _mm_add_ps(*pO, _mm_mul_ps(*pI, weight));
30828  }
30829 
30830  iFrame += frameCount4*4;
30831  } else
30832 #endif
30833  { /* Reference. */
30834  float weight0 = pRouter->config.weights[iChannelIn][iChannelOut];
30835  float weight1 = pRouter->config.weights[iChannelIn][iChannelOut];
30836  float weight2 = pRouter->config.weights[iChannelIn][iChannelOut];
30837  float weight3 = pRouter->config.weights[iChannelIn][iChannelOut];
30838  ma_uint64 frameCount4 = frameCount/4;
30839  ma_uint64 iFrame4;
30840 
30841  for (iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
30842  ppSamplesOut[iChannelOut][iFrame+0] += ppSamplesIn[iChannelIn][iFrame+0] * weight0;
30843  ppSamplesOut[iChannelOut][iFrame+1] += ppSamplesIn[iChannelIn][iFrame+1] * weight1;
30844  ppSamplesOut[iChannelOut][iFrame+2] += ppSamplesIn[iChannelIn][iFrame+2] * weight2;
30845  ppSamplesOut[iChannelOut][iFrame+3] += ppSamplesIn[iChannelIn][iFrame+3] * weight3;
30846  iFrame += 4;
30847  }
30848  }
30849 
30850  /* Leftover. */
30851  for (; iFrame < frameCount; ++iFrame) {
30852  ppSamplesOut[iChannelOut][iFrame] += ppSamplesIn[iChannelIn][iFrame] * pRouter->config.weights[iChannelIn][iChannelOut];
30853  }
30854  }
30855  }
30856  }
30857 }
30858 
30859 ma_uint64 ma_channel_router_read_deinterleaved(ma_channel_router* pRouter, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData)
30860 {
30861  if (pRouter == NULL || ppSamplesOut == NULL) {
30862  return 0;
30863  }
30864 
30865  /* Fast path for a passthrough. */
30866  if (pRouter->isPassthrough) {
30867  if (frameCount <= 0xFFFFFFFF) {
30868  return (ma_uint32)pRouter->config.onReadDeinterleaved(pRouter, (ma_uint32)frameCount, ppSamplesOut, pUserData);
30869  } else {
30870  float* ppNextSamplesOut[MA_MAX_CHANNELS];
30871  ma_uint64 totalFramesRead;
30872 
30873  ma_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(float*) * pRouter->config.channelsOut);
30874 
30875  totalFramesRead = 0;
30876  while (totalFramesRead < frameCount) {
30877  ma_uint32 iChannel;
30878  ma_uint32 framesJustRead;
30879  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
30880  ma_uint64 framesToReadRightNow = framesRemaining;
30881  if (framesToReadRightNow > 0xFFFFFFFF) {
30882  framesToReadRightNow = 0xFFFFFFFF;
30883  }
30884 
30885  framesJustRead = (ma_uint32)pRouter->config.onReadDeinterleaved(pRouter, (ma_uint32)framesToReadRightNow, (void**)ppNextSamplesOut, pUserData);
30886  if (framesJustRead == 0) {
30887  break;
30888  }
30889 
30890  totalFramesRead += framesJustRead;
30891 
30892  if (framesJustRead < framesToReadRightNow) {
30893  break;
30894  }
30895 
30896  for (iChannel = 0; iChannel < pRouter->config.channelsOut; ++iChannel) {
30897  ppNextSamplesOut[iChannel] += framesJustRead;
30898  }
30899  }
30900 
30901  return totalFramesRead;
30902  }
30903  }
30904 
30905  /* Slower path for a non-passthrough. */
30906  {
30907  float* ppNextSamplesOut[MA_MAX_CHANNELS];
30908  float* ppTemp[MA_MAX_CHANNELS];
30909  size_t maxBytesToReadPerFrameEachIteration;
30910  size_t maxFramesToReadEachIteration;
30911  ma_uint64 totalFramesRead;
30912  MA_ALIGN(MA_SIMD_ALIGNMENT) float temp[MA_MAX_CHANNELS * 256];
30913 
30914  ma_assert(sizeof(temp) <= 0xFFFFFFFF);
30915  ma_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(float*) * pRouter->config.channelsOut);
30916 
30917 
30918  ma_split_buffer(temp, sizeof(temp), pRouter->config.channelsIn, MA_SIMD_ALIGNMENT, (void**)&ppTemp, &maxBytesToReadPerFrameEachIteration);
30919 
30920  maxFramesToReadEachIteration = maxBytesToReadPerFrameEachIteration/sizeof(float);
30921 
30922  totalFramesRead = 0;
30923  while (totalFramesRead < frameCount) {
30924  ma_uint32 iChannel;
30925  ma_uint32 framesJustRead;
30926  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
30927  ma_uint64 framesToReadRightNow = framesRemaining;
30928  if (framesToReadRightNow > maxFramesToReadEachIteration) {
30929  framesToReadRightNow = maxFramesToReadEachIteration;
30930  }
30931 
30932  framesJustRead = pRouter->config.onReadDeinterleaved(pRouter, (ma_uint32)framesToReadRightNow, (void**)ppTemp, pUserData);
30933  if (framesJustRead == 0) {
30934  break;
30935  }
30936 
30937  ma_channel_router__do_routing(pRouter, framesJustRead, (float**)ppNextSamplesOut, (const float**)ppTemp); /* <-- Real work is done here. */
30938 
30939  totalFramesRead += framesJustRead;
30940  if (totalFramesRead < frameCount) {
30941  for (iChannel = 0; iChannel < pRouter->config.channelsIn; iChannel += 1) {
30942  ppNextSamplesOut[iChannel] += framesJustRead;
30943  }
30944  }
30945 
30946  if (framesJustRead < framesToReadRightNow) {
30947  break;
30948  }
30949  }
30950 
30951  return totalFramesRead;
30952  }
30953 }
30954 
30955 ma_channel_router_config ma_channel_router_config_init(ma_uint32 channelsIn, const ma_channel channelMapIn[MA_MAX_CHANNELS], ma_uint32 channelsOut, const ma_channel channelMapOut[MA_MAX_CHANNELS], ma_channel_mix_mode mixingMode, ma_channel_router_read_deinterleaved_proc onRead, void* pUserData)
30956 {
30958  ma_uint32 iChannel;
30959 
30960  ma_zero_object(&config);
30961 
30962  config.channelsIn = channelsIn;
30963  for (iChannel = 0; iChannel < channelsIn; ++iChannel) {
30964  config.channelMapIn[iChannel] = channelMapIn[iChannel];
30965  }
30966 
30967  config.channelsOut = channelsOut;
30968  for (iChannel = 0; iChannel < channelsOut; ++iChannel) {
30969  config.channelMapOut[iChannel] = channelMapOut[iChannel];
30970  }
30971 
30972  config.mixingMode = mixingMode;
30973  config.onReadDeinterleaved = onRead;
30974  config.pUserData = pUserData;
30975 
30976  return config;
30977 }
30978 
30979 
30980 
30981 /**************************************************************************************************************************************************************
30982 
30983 SRC
30984 
30985 **************************************************************************************************************************************************************/
30986 #define ma_floorf(x) ((float)floor((double)(x)))
30987 #define ma_sinf(x) ((float)sin((double)(x)))
30988 #define ma_cosf(x) ((float)cos((double)(x)))
30989 
30990 static MA_INLINE double ma_sinc(double x)
30991 {
30992  if (x != 0) {
30993  return sin(MA_PI_D*x) / (MA_PI_D*x);
30994  } else {
30995  return 1;
30996  }
30997 }
30998 
30999 #define ma_sincf(x) ((float)ma_sinc((double)(x)))
31000 
31001 
31002 ma_uint64 ma_src_read_deinterleaved__passthrough(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData);
31003 ma_uint64 ma_src_read_deinterleaved__linear(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData);
31004 ma_uint64 ma_src_read_deinterleaved__sinc(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData);
31005 
31006 void ma_src__build_sinc_table__sinc(ma_src* pSRC)
31007 {
31008  ma_uint32 i;
31009 
31010  ma_assert(pSRC != NULL);
31011 
31012  pSRC->sinc.table[0] = 1.0f;
31013  for (i = 1; i < ma_countof(pSRC->sinc.table); i += 1) {
31014  double x = i*MA_PI_D / MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION;
31015  pSRC->sinc.table[i] = (float)(sin(x)/x);
31016  }
31017 }
31018 
31019 void ma_src__build_sinc_table__rectangular(ma_src* pSRC)
31020 {
31021  /* This is the same as the base sinc table. */
31022  ma_src__build_sinc_table__sinc(pSRC);
31023 }
31024 
31025 void ma_src__build_sinc_table__hann(ma_src* pSRC)
31026 {
31027  ma_uint32 i;
31028 
31029  ma_src__build_sinc_table__sinc(pSRC);
31030 
31031  for (i = 0; i < ma_countof(pSRC->sinc.table); i += 1) {
31032  double x = pSRC->sinc.table[i];
31033  double N = MA_SRC_SINC_MAX_WINDOW_WIDTH*2;
31035  double w = 0.5 * (1 - cos((2*MA_PI_D*n) / (N)));
31036 
31037  pSRC->sinc.table[i] = (float)(x * w);
31038  }
31039 }
31040 
31041 ma_result ma_src_init(const ma_src_config* pConfig, ma_src* pSRC)
31042 {
31043  if (pSRC == NULL) {
31044  return MA_INVALID_ARGS;
31045  }
31046 
31047  ma_zero_object(pSRC);
31048 
31049  if (pConfig == NULL || pConfig->onReadDeinterleaved == NULL) {
31050  return MA_INVALID_ARGS;
31051  }
31052  if (pConfig->channels == 0 || pConfig->channels > MA_MAX_CHANNELS) {
31053  return MA_INVALID_ARGS;
31054  }
31055 
31056  pSRC->config = *pConfig;
31057 
31058  /* SIMD */
31059  pSRC->useSSE2 = ma_has_sse2() && !pConfig->noSSE2;
31060  pSRC->useAVX2 = ma_has_avx2() && !pConfig->noAVX2;
31061  pSRC->useAVX512 = ma_has_avx512f() && !pConfig->noAVX512;
31062  pSRC->useNEON = ma_has_neon() && !pConfig->noNEON;
31063 
31064  if (pSRC->config.algorithm == ma_src_algorithm_sinc) {
31065  /* Make sure the window width within bounds. */
31066  if (pSRC->config.sinc.windowWidth == 0) {
31067  pSRC->config.sinc.windowWidth = MA_SRC_SINC_DEFAULT_WINDOW_WIDTH;
31068  }
31069  if (pSRC->config.sinc.windowWidth < MA_SRC_SINC_MIN_WINDOW_WIDTH) {
31070  pSRC->config.sinc.windowWidth = MA_SRC_SINC_MIN_WINDOW_WIDTH;
31071  }
31072  if (pSRC->config.sinc.windowWidth > MA_SRC_SINC_MAX_WINDOW_WIDTH) {
31073  pSRC->config.sinc.windowWidth = MA_SRC_SINC_MAX_WINDOW_WIDTH;
31074  }
31075 
31076  /* Set up the lookup table. */
31077  switch (pSRC->config.sinc.windowFunction) {
31078  case ma_src_sinc_window_function_hann: ma_src__build_sinc_table__hann(pSRC); break;
31079  case ma_src_sinc_window_function_rectangular: ma_src__build_sinc_table__rectangular(pSRC); break;
31080  default: return MA_INVALID_ARGS; /* <-- Hitting this means the window function is unknown to miniaudio. */
31081  }
31082  }
31083 
31084  return MA_SUCCESS;
31085 }
31086 
31087 ma_result ma_src_set_sample_rate(ma_src* pSRC, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
31088 {
31089  if (pSRC == NULL) {
31090  return MA_INVALID_ARGS;
31091  }
31092 
31093  /* Must have a sample rate of > 0. */
31094  if (sampleRateIn == 0 || sampleRateOut == 0) {
31095  return MA_INVALID_ARGS;
31096  }
31097 
31098  ma_atomic_exchange_32(&pSRC->config.sampleRateIn, sampleRateIn);
31099  ma_atomic_exchange_32(&pSRC->config.sampleRateOut, sampleRateOut);
31100 
31101  return MA_SUCCESS;
31102 }
31103 
31104 ma_uint64 ma_src_read_deinterleaved(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData)
31105 {
31106  ma_src_algorithm algorithm;
31107 
31108  if (pSRC == NULL || frameCount == 0 || ppSamplesOut == NULL) {
31109  return 0;
31110  }
31111 
31112  algorithm = pSRC->config.algorithm;
31113 
31114  /* Can use a function pointer for this. */
31115  switch (algorithm) {
31116  case ma_src_algorithm_none: return ma_src_read_deinterleaved__passthrough(pSRC, frameCount, ppSamplesOut, pUserData);
31117  case ma_src_algorithm_linear: return ma_src_read_deinterleaved__linear( pSRC, frameCount, ppSamplesOut, pUserData);
31118  case ma_src_algorithm_sinc: return ma_src_read_deinterleaved__sinc( pSRC, frameCount, ppSamplesOut, pUserData);
31119  default: break;
31120  }
31121 
31122  /* Should never get here. */
31123  return 0;
31124 }
31125 
31126 ma_uint64 ma_src_read_deinterleaved__passthrough(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData)
31127 {
31128  if (frameCount <= 0xFFFFFFFF) {
31129  return pSRC->config.onReadDeinterleaved(pSRC, (ma_uint32)frameCount, ppSamplesOut, pUserData);
31130  } else {
31131  ma_uint32 iChannel;
31132  ma_uint64 totalFramesRead;
31133  float* ppNextSamplesOut[MA_MAX_CHANNELS];
31134 
31135  for (iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
31136  ppNextSamplesOut[iChannel] = (float*)ppSamplesOut[iChannel];
31137  }
31138 
31139  totalFramesRead = 0;
31140  while (totalFramesRead < frameCount) {
31141  ma_uint32 framesJustRead;
31142  ma_uint64 framesRemaining = frameCount - totalFramesRead;
31143  ma_uint64 framesToReadRightNow = framesRemaining;
31144  if (framesToReadRightNow > 0xFFFFFFFF) {
31145  framesToReadRightNow = 0xFFFFFFFF;
31146  }
31147 
31148  framesJustRead = (ma_uint32)pSRC->config.onReadDeinterleaved(pSRC, (ma_uint32)framesToReadRightNow, (void**)ppNextSamplesOut, pUserData);
31149  if (framesJustRead == 0) {
31150  break;
31151  }
31152 
31153  totalFramesRead += framesJustRead;
31154  for (iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
31155  ppNextSamplesOut[iChannel] += framesJustRead;
31156  }
31157 
31158  if (framesJustRead < framesToReadRightNow) {
31159  break;
31160  }
31161  }
31162 
31163  return totalFramesRead;
31164  }
31165 }
31166 
31167 ma_uint64 ma_src_read_deinterleaved__linear(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData)
31168 {
31169  float* ppNextSamplesOut[MA_MAX_CHANNELS];
31170  float factor;
31171  ma_uint32 maxFrameCountPerChunkIn;
31172  ma_uint64 totalFramesRead;
31173 
31174  ma_assert(pSRC != NULL);
31175  ma_assert(frameCount > 0);
31176  ma_assert(ppSamplesOut != NULL);
31177 
31178  ma_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(void*) * pSRC->config.channels);
31179 
31180  factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut;
31181  maxFrameCountPerChunkIn = ma_countof(pSRC->linear.input[0]);
31182 
31183  totalFramesRead = 0;
31184  while (totalFramesRead < frameCount) {
31185  ma_uint32 iChannel;
31186  float tBeg;
31187  float tEnd;
31188  float tAvailable;
31189  float tNext;
31190  float* ppSamplesFromClient[MA_MAX_CHANNELS];
31191  ma_uint32 iNextFrame;
31192  ma_uint32 maxOutputFramesToRead;
31193  ma_uint32 maxOutputFramesToRead4;
31194  ma_uint32 framesToReadFromClient;
31195  ma_uint32 framesReadFromClient;
31196  ma_uint64 framesRemaining = frameCount - totalFramesRead;
31197  ma_uint64 framesToRead = framesRemaining;
31198  if (framesToRead > 16384) {
31199  framesToRead = 16384; /* <-- Keep this small because we're using 32-bit floats for calculating sample positions and I don't want to run out of precision with huge sample counts. */
31200  }
31201 
31202 
31203  /* Read Input Data */
31204  tBeg = pSRC->linear.timeIn;
31205  tEnd = tBeg + ((ma_int64)framesToRead*factor); /* Cast to int64 required for VC6. */
31206 
31207  framesToReadFromClient = (ma_uint32)(tEnd) + 1 + 1; /* +1 to make tEnd 1-based and +1 because we always need to an extra sample for interpolation. */
31208  if (framesToReadFromClient >= maxFrameCountPerChunkIn) {
31209  framesToReadFromClient = maxFrameCountPerChunkIn;
31210  }
31211 
31212  for (iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
31213  ppSamplesFromClient[iChannel] = pSRC->linear.input[iChannel] + pSRC->linear.leftoverFrames;
31214  }
31215 
31216  framesReadFromClient = 0;
31217  if (framesToReadFromClient > pSRC->linear.leftoverFrames) {
31218  framesReadFromClient = (ma_uint32)pSRC->config.onReadDeinterleaved(pSRC, (ma_uint32)framesToReadFromClient - pSRC->linear.leftoverFrames, (void**)ppSamplesFromClient, pUserData);
31219  }
31220 
31221  framesReadFromClient += pSRC->linear.leftoverFrames; /* <-- You can sort of think of it as though we've re-read the leftover samples from the client. */
31222  if (framesReadFromClient < 2) {
31223  break;
31224  }
31225 
31226  for (iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
31227  ppSamplesFromClient[iChannel] = pSRC->linear.input[iChannel];
31228  }
31229 
31230 
31231  /* Write Output Data */
31232 
31233  /*
31234  At this point we have a bunch of frames that the client has given to us for processing. From this we can determine the maximum number of output frames
31235  that can be processed from this input. We want to output as many samples as possible from our input data.
31236  */
31237  tAvailable = framesReadFromClient - tBeg - 1; /* Subtract 1 because the last input sample is needed for interpolation and cannot be included in the output sample count calculation. */
31238 
31239  maxOutputFramesToRead = (ma_uint32)(tAvailable / factor);
31240  if (maxOutputFramesToRead == 0) {
31241  maxOutputFramesToRead = 1;
31242  }
31243  if (maxOutputFramesToRead > framesToRead) {
31244  maxOutputFramesToRead = (ma_uint32)framesToRead;
31245  }
31246 
31247  /* Output frames are always read in groups of 4 because I'm planning on using this as a reference for some SIMD-y stuff later. */
31248  maxOutputFramesToRead4 = maxOutputFramesToRead/4;
31249  for (iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
31250  ma_uint32 iFrameOut;
31251  float t0 = pSRC->linear.timeIn + factor*0;
31252  float t1 = pSRC->linear.timeIn + factor*1;
31253  float t2 = pSRC->linear.timeIn + factor*2;
31254  float t3 = pSRC->linear.timeIn + factor*3;
31255  float t;
31256 
31257  for (iFrameOut = 0; iFrameOut < maxOutputFramesToRead4; iFrameOut += 1) {
31258  float iPrevSample0 = (float)floor(t0);
31259  float iPrevSample1 = (float)floor(t1);
31260  float iPrevSample2 = (float)floor(t2);
31261  float iPrevSample3 = (float)floor(t3);
31262 
31263  float iNextSample0 = iPrevSample0 + 1;
31264  float iNextSample1 = iPrevSample1 + 1;
31265  float iNextSample2 = iPrevSample2 + 1;
31266  float iNextSample3 = iPrevSample3 + 1;
31267 
31268  float alpha0 = t0 - iPrevSample0;
31269  float alpha1 = t1 - iPrevSample1;
31270  float alpha2 = t2 - iPrevSample2;
31271  float alpha3 = t3 - iPrevSample3;
31272 
31273  float prevSample0 = ppSamplesFromClient[iChannel][(ma_uint32)iPrevSample0];
31274  float prevSample1 = ppSamplesFromClient[iChannel][(ma_uint32)iPrevSample1];
31275  float prevSample2 = ppSamplesFromClient[iChannel][(ma_uint32)iPrevSample2];
31276  float prevSample3 = ppSamplesFromClient[iChannel][(ma_uint32)iPrevSample3];
31277 
31278  float nextSample0 = ppSamplesFromClient[iChannel][(ma_uint32)iNextSample0];
31279  float nextSample1 = ppSamplesFromClient[iChannel][(ma_uint32)iNextSample1];
31280  float nextSample2 = ppSamplesFromClient[iChannel][(ma_uint32)iNextSample2];
31281  float nextSample3 = ppSamplesFromClient[iChannel][(ma_uint32)iNextSample3];
31282 
31283  ppNextSamplesOut[iChannel][iFrameOut*4 + 0] = ma_mix_f32_fast(prevSample0, nextSample0, alpha0);
31284  ppNextSamplesOut[iChannel][iFrameOut*4 + 1] = ma_mix_f32_fast(prevSample1, nextSample1, alpha1);
31285  ppNextSamplesOut[iChannel][iFrameOut*4 + 2] = ma_mix_f32_fast(prevSample2, nextSample2, alpha2);
31286  ppNextSamplesOut[iChannel][iFrameOut*4 + 3] = ma_mix_f32_fast(prevSample3, nextSample3, alpha3);
31287 
31288  t0 += factor*4;
31289  t1 += factor*4;
31290  t2 += factor*4;
31291  t3 += factor*4;
31292  }
31293 
31294  t = pSRC->linear.timeIn + (factor*maxOutputFramesToRead4*4);
31295  for (iFrameOut = (maxOutputFramesToRead4*4); iFrameOut < maxOutputFramesToRead; iFrameOut += 1) {
31296  float iPrevSample = (float)floor(t);
31297  float iNextSample = iPrevSample + 1;
31298  float alpha = t - iPrevSample;
31299  float prevSample;
31300  float nextSample;
31301 
31302  ma_assert(iPrevSample < ma_countof(pSRC->linear.input[iChannel]));
31303  ma_assert(iNextSample < ma_countof(pSRC->linear.input[iChannel]));
31304 
31305  prevSample = ppSamplesFromClient[iChannel][(ma_uint32)iPrevSample];
31306  nextSample = ppSamplesFromClient[iChannel][(ma_uint32)iNextSample];
31307 
31308  ppNextSamplesOut[iChannel][iFrameOut] = ma_mix_f32_fast(prevSample, nextSample, alpha);
31309 
31310  t += factor;
31311  }
31312 
31313  ppNextSamplesOut[iChannel] += maxOutputFramesToRead;
31314  }
31315 
31316  totalFramesRead += maxOutputFramesToRead;
31317 
31318 
31319  /* Residual */
31320  tNext = pSRC->linear.timeIn + (maxOutputFramesToRead*factor);
31321 
31322  pSRC->linear.timeIn = tNext;
31323  ma_assert(tNext <= framesReadFromClient+1);
31324 
31325  iNextFrame = (ma_uint32)floor(tNext);
31326  pSRC->linear.leftoverFrames = framesReadFromClient - iNextFrame;
31327  pSRC->linear.timeIn = tNext - iNextFrame;
31328 
31329  for (iChannel = 0; iChannel < pSRC->config.channels; ++iChannel) {
31330  ma_uint32 iFrame;
31331  for (iFrame = 0; iFrame < pSRC->linear.leftoverFrames; ++iFrame) {
31332  float sample = ppSamplesFromClient[iChannel][framesReadFromClient-pSRC->linear.leftoverFrames + iFrame];
31333  ppSamplesFromClient[iChannel][iFrame] = sample;
31334  }
31335  }
31336 
31337 
31338  /* Exit the loop if we've found everything from the client. */
31339  if (framesReadFromClient < framesToReadFromClient) {
31340  break;
31341  }
31342  }
31343 
31344  return totalFramesRead;
31345 }
31346 
31347 
31349 {
31351  ma_zero_object(&config);
31352 
31353  return config;
31354 }
31355 
31356 ma_src_config ma_src_config_init(ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_uint32 channels, ma_src_read_deinterleaved_proc onReadDeinterleaved, void* pUserData)
31357 {
31359  config.sampleRateIn = sampleRateIn;
31360  config.sampleRateOut = sampleRateOut;
31361  config.channels = channels;
31362  config.onReadDeinterleaved = onReadDeinterleaved;
31363  config.pUserData = pUserData;
31364 
31365  return config;
31366 }
31367 
31368 
31369 /**************************************************************************************************************************************************************
31370 
31371 Sinc Sample Rate Conversion
31372 ===========================
31373 
31374 The sinc SRC algorithm uses a windowed sinc to perform interpolation of samples. Currently, miniaudio's implementation supports rectangular and Hann window
31375 methods.
31376 
31377 Whenever an output sample is being computed, it looks at a sub-section of the input samples. I've called this sub-section in the code below the "window",
31378 which I realize is a bit ambigous with the mathematical "window", but it works for me when I need to conceptualize things in my head. The window is made up
31379 of two halves. The first half contains past input samples (initialized to zero), and the second half contains future input samples. As time moves forward
31380 and input samples are consumed, the window moves forward. The larger the window, the better the quality at the expense of slower processing. The window is
31381 limited the range [MA_SRC_SINC_MIN_WINDOW_WIDTH, MA_SRC_SINC_MAX_WINDOW_WIDTH] and defaults to MA_SRC_SINC_DEFAULT_WINDOW_WIDTH.
31382 
31383 Input samples are cached for efficiency (to prevent frequently requesting tiny numbers of samples from the client). When the window gets to the end of the
31384 cache, it's moved back to the start, and more samples are read from the client. If the client has no more data to give, the cache is filled with zeros and
31385 the last of the input samples will be consumed. Once the last of the input samples have been consumed, no more samples will be output.
31386 
31387 
31388 When reading output samples, we always first read whatever is already in the input cache. Only when the cache has been fully consumed do we read more data
31389 from the client.
31390 
31391 To access samples in the input buffer you do so relative to the window. When the window itself is at position 0, the first item in the buffer is accessed
31392 with "windowPos + windowWidth". Generally, to access any sample relative to the window you do "windowPos + windowWidth + sampleIndexRelativeToWindow".
31393 
31394 **************************************************************************************************************************************************************/
31395 
31396 /* Comment this to disable interpolation of table lookups. Less accurate, but faster. */
31397 #define MA_USE_SINC_TABLE_INTERPOLATION
31398 
31399 /* Retrieves a sample from the input buffer's window. Values >= 0 retrieve future samples. Negative values return past samples. */
31400 static MA_INLINE float ma_src_sinc__get_input_sample_from_window(const ma_src* pSRC, ma_uint32 channel, ma_uint32 windowPosInSamples, ma_int32 sampleIndex)
31401 {
31402  ma_assert(pSRC != NULL);
31403  ma_assert(channel < pSRC->config.channels);
31404  ma_assert(sampleIndex >= -(ma_int32)pSRC->config.sinc.windowWidth);
31405  ma_assert(sampleIndex < (ma_int32)pSRC->config.sinc.windowWidth);
31406 
31407  /* The window should always be contained within the input cache. */
31408  ma_assert(windowPosInSamples < ma_countof(pSRC->sinc.input[0]) - pSRC->config.sinc.windowWidth);
31409 
31410  return pSRC->sinc.input[channel][windowPosInSamples + pSRC->config.sinc.windowWidth + sampleIndex];
31411 }
31412 
31413 static MA_INLINE float ma_src_sinc__interpolation_factor(const ma_src* pSRC, float x)
31414 {
31415  float xabs;
31416  ma_int32 ixabs;
31417 
31418  ma_assert(pSRC != NULL);
31419 
31420  xabs = (float)fabs(x);
31422  ixabs = (ma_int32)xabs;
31423 
31424 #if defined(MA_USE_SINC_TABLE_INTERPOLATION)
31425  {
31426  float a = xabs - ixabs;
31427  return ma_mix_f32_fast(pSRC->sinc.table[ixabs], pSRC->sinc.table[ixabs+1], a);
31428  }
31429 #else
31430  return pSRC->sinc.table[ixabs];
31431 #endif
31432 }
31433 
31434 #if defined(MA_SUPPORT_SSE2)
31435 static MA_INLINE __m128 ma_fabsf_sse2(__m128 x)
31436 {
31437  return _mm_and_ps(_mm_castsi128_ps(_mm_set1_epi32(0x7FFFFFFF)), x);
31438 }
31439 
31440 static MA_INLINE __m128 ma_truncf_sse2(__m128 x)
31441 {
31442  return _mm_cvtepi32_ps(_mm_cvttps_epi32(x));
31443 }
31444 
31445 static MA_INLINE __m128 ma_src_sinc__interpolation_factor__sse2(const ma_src* pSRC, __m128 x)
31446 {
31447  __m128 resolution128;
31448  __m128 xabs;
31449  __m128i ixabs;
31450  __m128 lo;
31451  __m128 hi;
31452  __m128 a;
31453  __m128 r;
31454  int* ixabsv;
31455 
31456  resolution128 = _mm_set1_ps(MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION);
31457  xabs = ma_fabsf_sse2(x);
31458  xabs = _mm_mul_ps(xabs, resolution128);
31459  ixabs = _mm_cvttps_epi32(xabs);
31460 
31461  ixabsv = (int*)&ixabs;
31462 
31463  lo = _mm_set_ps(
31464  pSRC->sinc.table[ixabsv[3]],
31465  pSRC->sinc.table[ixabsv[2]],
31466  pSRC->sinc.table[ixabsv[1]],
31467  pSRC->sinc.table[ixabsv[0]]
31468  );
31469 
31470  hi = _mm_set_ps(
31471  pSRC->sinc.table[ixabsv[3]+1],
31472  pSRC->sinc.table[ixabsv[2]+1],
31473  pSRC->sinc.table[ixabsv[1]+1],
31474  pSRC->sinc.table[ixabsv[0]+1]
31475  );
31476 
31477  a = _mm_sub_ps(xabs, _mm_cvtepi32_ps(ixabs));
31478  r = ma_mix_f32_fast__sse2(lo, hi, a);
31479 
31480  return r;
31481 }
31482 #endif
31483 
31484 #if defined(MA_SUPPORT_AVX2)
31485 static MA_INLINE __m256 ma_fabsf_avx2(__m256 x)
31486 {
31487  return _mm256_and_ps(_mm256_castsi256_ps(_mm256_set1_epi32(0x7FFFFFFF)), x);
31488 }
31489 
31490 #if 0
31491 static MA_INLINE __m256 ma_src_sinc__interpolation_factor__avx2(const ma_src* pSRC, __m256 x)
31492 {
31493  __m256 resolution256 = _mm256_set1_ps(MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION);
31494  __m256 xabs = ma_fabsf_avx2(x);
31495 
31496  xabs = _mm256_mul_ps(xabs, resolution256);
31497 
31498  __m256i ixabs = _mm256_cvttps_epi32(xabs);
31499  __m256 a = _mm256_sub_ps(xabs, _mm256_cvtepi32_ps(ixabs));
31500 
31501 
31502  int* ixabsv = (int*)&ixabs;
31503 
31504  __m256 lo = _mm256_set_ps(
31505  pSRC->sinc.table[ixabsv[7]],
31506  pSRC->sinc.table[ixabsv[6]],
31507  pSRC->sinc.table[ixabsv[5]],
31508  pSRC->sinc.table[ixabsv[4]],
31509  pSRC->sinc.table[ixabsv[3]],
31510  pSRC->sinc.table[ixabsv[2]],
31511  pSRC->sinc.table[ixabsv[1]],
31512  pSRC->sinc.table[ixabsv[0]]
31513  );
31514 
31515  __m256 hi = _mm256_set_ps(
31516  pSRC->sinc.table[ixabsv[7]+1],
31517  pSRC->sinc.table[ixabsv[6]+1],
31518  pSRC->sinc.table[ixabsv[5]+1],
31519  pSRC->sinc.table[ixabsv[4]+1],
31520  pSRC->sinc.table[ixabsv[3]+1],
31521  pSRC->sinc.table[ixabsv[2]+1],
31522  pSRC->sinc.table[ixabsv[1]+1],
31523  pSRC->sinc.table[ixabsv[0]+1]
31524  );
31525 
31526  __m256 r = ma_mix_f32_fast__avx2(lo, hi, a);
31527 
31528  return r;
31529 }
31530 #endif
31531 
31532 #endif
31533 
31534 #if defined(MA_SUPPORT_NEON)
31535 static MA_INLINE float32x4_t ma_fabsf_neon(float32x4_t x)
31536 {
31537  return vabdq_f32(vmovq_n_f32(0), x);
31538 }
31539 
31540 static MA_INLINE float32x4_t ma_src_sinc__interpolation_factor__neon(const ma_src* pSRC, float32x4_t x)
31541 {
31542  float32x4_t xabs;
31543  int32x4_t ixabs;
31544  float32x4_t a;
31545  float32x4_t r;
31546  int* ixabsv;
31547  float lo[4];
31548  float hi[4];
31549 
31550  xabs = ma_fabsf_neon(x);
31551  xabs = vmulq_n_f32(xabs, MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION);
31552  ixabs = vcvtq_s32_f32(xabs);
31553 
31554  ixabsv = (int*)&ixabs;
31555 
31556  lo[0] = pSRC->sinc.table[ixabsv[0]];
31557  lo[1] = pSRC->sinc.table[ixabsv[1]];
31558  lo[2] = pSRC->sinc.table[ixabsv[2]];
31559  lo[3] = pSRC->sinc.table[ixabsv[3]];
31560 
31561  hi[0] = pSRC->sinc.table[ixabsv[0]+1];
31562  hi[1] = pSRC->sinc.table[ixabsv[1]+1];
31563  hi[2] = pSRC->sinc.table[ixabsv[2]+1];
31564  hi[3] = pSRC->sinc.table[ixabsv[3]+1];
31565 
31566  a = vsubq_f32(xabs, vcvtq_f32_s32(ixabs));
31567  r = ma_mix_f32_fast__neon(vld1q_f32(lo), vld1q_f32(hi), a);
31568 
31569  return r;
31570 }
31571 #endif
31572 
31573 ma_uint64 ma_src_read_deinterleaved__sinc(ma_src* pSRC, ma_uint64 frameCount, void** ppSamplesOut, void* pUserData)
31574 {
31575  float factor;
31576  float inverseFactor;
31577  ma_int32 windowWidth;
31578  ma_int32 windowWidth2;
31579  ma_int32 windowWidthSIMD;
31580  ma_int32 windowWidthSIMD2;
31581  float* ppNextSamplesOut[MA_MAX_CHANNELS];
31582  float _windowSamplesUnaligned[MA_SRC_SINC_MAX_WINDOW_WIDTH*2 + MA_SIMD_ALIGNMENT];
31583  float* windowSamples;
31584  float _iWindowFUnaligned[MA_SRC_SINC_MAX_WINDOW_WIDTH*2 + MA_SIMD_ALIGNMENT];
31585  float* iWindowF;
31586  ma_int32 i;
31587  ma_uint64 totalOutputFramesRead;
31588 
31589  ma_assert(pSRC != NULL);
31590  ma_assert(frameCount > 0);
31591  ma_assert(ppSamplesOut != NULL);
31592 
31593  factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut;
31594  inverseFactor = 1/factor;
31595 
31596  windowWidth = (ma_int32)pSRC->config.sinc.windowWidth;
31597  windowWidth2 = windowWidth*2;
31598 
31599  /*
31600  There are cases where it's actually more efficient to increase the window width so that it's aligned with the respective
31601  SIMD pipeline being used.
31602  */
31603  windowWidthSIMD = windowWidth;
31604  if (pSRC->useNEON) {
31605  windowWidthSIMD = (windowWidthSIMD + 1) & ~(1);
31606  } else if (pSRC->useAVX512) {
31607  windowWidthSIMD = (windowWidthSIMD + 7) & ~(7);
31608  } else if (pSRC->useAVX2) {
31609  windowWidthSIMD = (windowWidthSIMD + 3) & ~(3);
31610  } else if (pSRC->useSSE2) {
31611  windowWidthSIMD = (windowWidthSIMD + 1) & ~(1);
31612  }
31613 
31614  windowWidthSIMD2 = windowWidthSIMD*2;
31615  (void)windowWidthSIMD2; /* <-- Silence a warning when SIMD is disabled. */
31616 
31617  ma_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(void*) * pSRC->config.channels);
31618 
31619  windowSamples = (float*)(((ma_uintptr)_windowSamplesUnaligned + MA_SIMD_ALIGNMENT-1) & ~(MA_SIMD_ALIGNMENT-1));
31620  ma_zero_memory(windowSamples, MA_SRC_SINC_MAX_WINDOW_WIDTH*2 * sizeof(float));
31621 
31622  iWindowF = (float*)(((ma_uintptr)_iWindowFUnaligned + MA_SIMD_ALIGNMENT-1) & ~(MA_SIMD_ALIGNMENT-1));
31623  ma_zero_memory(iWindowF, MA_SRC_SINC_MAX_WINDOW_WIDTH*2 * sizeof(float));
31624 
31625  for (i = 0; i < windowWidth2; ++i) {
31626  iWindowF[i] = (float)(i - windowWidth);
31627  }
31628 
31629  totalOutputFramesRead = 0;
31630  while (totalOutputFramesRead < frameCount) {
31631  ma_uint32 maxInputSamplesAvailableInCache;
31632  float timeInBeg;
31633  float timeInEnd;
31634  ma_uint64 maxOutputFramesToRead;
31635  ma_uint64 outputFramesRemaining;
31636  ma_uint64 outputFramesToRead;
31637  ma_uint32 iChannel;
31638  ma_uint32 prevWindowPosInSamples;
31639  ma_uint32 availableOutputFrames;
31640 
31641  /*
31642  The maximum number of frames we can read this iteration depends on how many input samples we have available to us. This is the number
31643  of input samples between the end of the window and the end of the cache.
31644  */
31645  maxInputSamplesAvailableInCache = ma_countof(pSRC->sinc.input[0]) - (pSRC->config.sinc.windowWidth*2) - pSRC->sinc.windowPosInSamples;
31646  if (maxInputSamplesAvailableInCache > pSRC->sinc.inputFrameCount) {
31647  maxInputSamplesAvailableInCache = pSRC->sinc.inputFrameCount;
31648  }
31649 
31650  /* Never consume the tail end of the input data if requested. */
31651  if (pSRC->config.neverConsumeEndOfInput) {
31652  if (maxInputSamplesAvailableInCache >= pSRC->config.sinc.windowWidth) {
31653  maxInputSamplesAvailableInCache -= pSRC->config.sinc.windowWidth;
31654  } else {
31655  maxInputSamplesAvailableInCache = 0;
31656  }
31657  }
31658 
31659  timeInBeg = pSRC->sinc.timeIn;
31660  timeInEnd = (float)(pSRC->sinc.windowPosInSamples + maxInputSamplesAvailableInCache);
31661 
31662  ma_assert(timeInBeg >= 0);
31663  ma_assert(timeInBeg <= timeInEnd);
31664 
31665  maxOutputFramesToRead = (ma_uint64)(((timeInEnd - timeInBeg) * inverseFactor));
31666 
31667  outputFramesRemaining = frameCount - totalOutputFramesRead;
31668  outputFramesToRead = outputFramesRemaining;
31669  if (outputFramesToRead > maxOutputFramesToRead) {
31670  outputFramesToRead = maxOutputFramesToRead;
31671  }
31672 
31673  for (iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) {
31674  /* Do SRC. */
31675  float timeIn = timeInBeg;
31676  ma_uint32 iSample;
31677  for (iSample = 0; iSample < outputFramesToRead; iSample += 1) {
31678  float sampleOut = 0;
31679  float iTimeInF = ma_floorf(timeIn);
31680  ma_uint32 iTimeIn = (ma_uint32)iTimeInF;
31681  ma_int32 iWindow = 0;
31682  float tScalar;
31683 
31684  /* Pre-load the window samples into an aligned buffer to begin with. Need to put these into an aligned buffer to make SIMD easier. */
31685  windowSamples[0] = 0; /* <-- The first sample is always zero. */
31686  for (i = 1; i < windowWidth2; ++i) {
31687  windowSamples[i] = pSRC->sinc.input[iChannel][iTimeIn + i];
31688  }
31689 
31690 #if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX512)
31691  if (pSRC->useAVX2 || pSRC->useAVX512) {
31692  __m256i ixabs[MA_SRC_SINC_MAX_WINDOW_WIDTH*2/8];
31693  __m256 a[MA_SRC_SINC_MAX_WINDOW_WIDTH*2/8];
31694  __m256 resolution256;
31695  __m256 t;
31696  __m256 r;
31697  ma_int32 windowWidth8;
31698  ma_int32 iWindow8;
31699 
31700  resolution256 = _mm256_set1_ps(MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION);
31701 
31702  t = _mm256_set1_ps((timeIn - iTimeInF));
31703  r = _mm256_set1_ps(0);
31704 
31705  windowWidth8 = windowWidthSIMD2 >> 3;
31706  for (iWindow8 = 0; iWindow8 < windowWidth8; iWindow8 += 1) {
31707  __m256 w = *((__m256*)iWindowF + iWindow8);
31708 
31709  __m256 xabs = _mm256_sub_ps(t, w);
31710  xabs = ma_fabsf_avx2(xabs);
31711  xabs = _mm256_mul_ps(xabs, resolution256);
31712 
31713  ixabs[iWindow8] = _mm256_cvttps_epi32(xabs);
31714  a[iWindow8] = _mm256_sub_ps(xabs, _mm256_cvtepi32_ps(ixabs[iWindow8]));
31715  }
31716 
31717  for (iWindow8 = 0; iWindow8 < windowWidth8; iWindow8 += 1) {
31718  int* ixabsv = (int*)&ixabs[iWindow8];
31719 
31720  __m256 lo = _mm256_set_ps(
31721  pSRC->sinc.table[ixabsv[7]],
31722  pSRC->sinc.table[ixabsv[6]],
31723  pSRC->sinc.table[ixabsv[5]],
31724  pSRC->sinc.table[ixabsv[4]],
31725  pSRC->sinc.table[ixabsv[3]],
31726  pSRC->sinc.table[ixabsv[2]],
31727  pSRC->sinc.table[ixabsv[1]],
31728  pSRC->sinc.table[ixabsv[0]]
31729  );
31730 
31731  __m256 hi = _mm256_set_ps(
31732  pSRC->sinc.table[ixabsv[7]+1],
31733  pSRC->sinc.table[ixabsv[6]+1],
31734  pSRC->sinc.table[ixabsv[5]+1],
31735  pSRC->sinc.table[ixabsv[4]+1],
31736  pSRC->sinc.table[ixabsv[3]+1],
31737  pSRC->sinc.table[ixabsv[2]+1],
31738  pSRC->sinc.table[ixabsv[1]+1],
31739  pSRC->sinc.table[ixabsv[0]+1]
31740  );
31741 
31742  __m256 s = *((__m256*)windowSamples + iWindow8);
31743  r = _mm256_add_ps(r, _mm256_mul_ps(s, ma_mix_f32_fast__avx2(lo, hi, a[iWindow8])));
31744  }
31745 
31746  /* Horizontal add. */
31747  __m256 x = _mm256_hadd_ps(r, _mm256_permute2f128_ps(r, r, 1));
31748  x = _mm256_hadd_ps(x, x);
31749  x = _mm256_hadd_ps(x, x);
31750  sampleOut += _mm_cvtss_f32(_mm256_castps256_ps128(x));
31751 
31752  iWindow += windowWidth8 * 8;
31753  }
31754  else
31755 #endif
31756 #if defined(MA_SUPPORT_SSE2)
31757  if (pSRC->useSSE2) {
31758  __m128 t = _mm_set1_ps((timeIn - iTimeInF));
31759  __m128 r = _mm_set1_ps(0);
31760 
31761  ma_int32 windowWidth4 = windowWidthSIMD2 >> 2;
31762  ma_int32 iWindow4;
31763  for (iWindow4 = 0; iWindow4 < windowWidth4; iWindow4 += 1) {
31764  __m128* s = (__m128*)windowSamples + iWindow4;
31765  __m128* w = (__m128*)iWindowF + iWindow4;
31766 
31767  __m128 a = ma_src_sinc__interpolation_factor__sse2(pSRC, _mm_sub_ps(t, *w));
31768  r = _mm_add_ps(r, _mm_mul_ps(*s, a));
31769  }
31770 
31771  sampleOut += ((float*)(&r))[0];
31772  sampleOut += ((float*)(&r))[1];
31773  sampleOut += ((float*)(&r))[2];
31774  sampleOut += ((float*)(&r))[3];
31775 
31776  iWindow += windowWidth4 * 4;
31777  }
31778  else
31779 #endif
31780 #if defined(MA_SUPPORT_NEON)
31781  if (pSRC->useNEON) {
31782  float32x4_t t = vmovq_n_f32((timeIn - iTimeInF));
31783  float32x4_t r = vmovq_n_f32(0);
31784 
31785  ma_int32 windowWidth4 = windowWidthSIMD2 >> 2;
31786  ma_int32 iWindow4;
31787  for (iWindow4 = 0; iWindow4 < windowWidth4; iWindow4 += 1) {
31788  float32x4_t* s = (float32x4_t*)windowSamples + iWindow4;
31789  float32x4_t* w = (float32x4_t*)iWindowF + iWindow4;
31790 
31791  float32x4_t a = ma_src_sinc__interpolation_factor__neon(pSRC, vsubq_f32(t, *w));
31792  r = vaddq_f32(r, vmulq_f32(*s, a));
31793  }
31794 
31795  sampleOut += ((float*)(&r))[0];
31796  sampleOut += ((float*)(&r))[1];
31797  sampleOut += ((float*)(&r))[2];
31798  sampleOut += ((float*)(&r))[3];
31799 
31800  iWindow += windowWidth4 * 4;
31801  }
31802  else
31803 #endif
31804  {
31805  iWindow += 1; /* The first one is a dummy for SIMD alignment purposes. Skip it. */
31806  }
31807 
31808  /* Non-SIMD/Reference implementation. */
31809  tScalar = (timeIn - iTimeIn);
31810  for (; iWindow < windowWidth2; iWindow += 1) {
31811  float s = windowSamples[iWindow];
31812  float w = iWindowF[iWindow];
31813 
31814  float a = ma_src_sinc__interpolation_factor(pSRC, (tScalar - w));
31815  float r = s * a;
31816 
31817  sampleOut += r;
31818  }
31819 
31820  ppNextSamplesOut[iChannel][iSample] = (float)sampleOut;
31821 
31822  timeIn += factor;
31823  }
31824 
31825  ppNextSamplesOut[iChannel] += outputFramesToRead;
31826  }
31827 
31828  totalOutputFramesRead += outputFramesToRead;
31829 
31830  prevWindowPosInSamples = pSRC->sinc.windowPosInSamples;
31831 
31832  pSRC->sinc.timeIn += ((ma_int64)outputFramesToRead * factor); /* Cast to int64 required for VC6. */
31833  pSRC->sinc.windowPosInSamples = (ma_uint32)pSRC->sinc.timeIn;
31834  pSRC->sinc.inputFrameCount -= pSRC->sinc.windowPosInSamples - prevWindowPosInSamples;
31835 
31836  /* If the window has reached a point where we cannot read a whole output sample it needs to be moved back to the start. */
31837  availableOutputFrames = (ma_uint32)((timeInEnd - pSRC->sinc.timeIn) * inverseFactor);
31838 
31839  if (availableOutputFrames == 0) {
31840  size_t samplesToMove = ma_countof(pSRC->sinc.input[0]) - pSRC->sinc.windowPosInSamples;
31841 
31842  pSRC->sinc.timeIn -= ma_floorf(pSRC->sinc.timeIn);
31843  pSRC->sinc.windowPosInSamples = 0;
31844 
31845  /* Move everything from the end of the cache up to the front. */
31846  for (iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) {
31847  memmove(pSRC->sinc.input[iChannel], pSRC->sinc.input[iChannel] + ma_countof(pSRC->sinc.input[iChannel]) - samplesToMove, samplesToMove * sizeof(*pSRC->sinc.input[iChannel]));
31848  }
31849  }
31850 
31851  /* Read more data from the client if required. */
31852  if (pSRC->isEndOfInputLoaded) {
31853  pSRC->isEndOfInputLoaded = MA_FALSE;
31854  break;
31855  }
31856 
31857  /*
31858  Everything beyond this point is reloading. If we're at the end of the input data we do _not_ want to try reading any more in this function call. If the
31859  caller wants to keep trying, they can reload their internal data sources and call this function again. We should never be
31860  */
31861  ma_assert(pSRC->isEndOfInputLoaded == MA_FALSE);
31862 
31863  if (pSRC->sinc.inputFrameCount <= pSRC->config.sinc.windowWidth || availableOutputFrames == 0) {
31864  float* ppInputDst[MA_MAX_CHANNELS] = {0};
31865  ma_uint32 framesToReadFromClient;
31866  ma_uint32 framesReadFromClient;
31867  ma_uint32 leftoverFrames;
31868 
31869  for (iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) {
31870  ppInputDst[iChannel] = pSRC->sinc.input[iChannel] + pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount;
31871  }
31872 
31873  /* Now read data from the client. */
31874  framesToReadFromClient = ma_countof(pSRC->sinc.input[0]) - (pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount);
31875 
31876  framesReadFromClient = 0;
31877  if (framesToReadFromClient > 0) {
31878  framesReadFromClient = pSRC->config.onReadDeinterleaved(pSRC, framesToReadFromClient, (void**)ppInputDst, pUserData);
31879  }
31880 
31881  if (framesReadFromClient != framesToReadFromClient) {
31882  pSRC->isEndOfInputLoaded = MA_TRUE;
31883  } else {
31884  pSRC->isEndOfInputLoaded = MA_FALSE;
31885  }
31886 
31887  if (framesReadFromClient != 0) {
31888  pSRC->sinc.inputFrameCount += framesReadFromClient;
31889  } else {
31890  /* We couldn't get anything more from the client. If no more output samples can be computed from the available input samples we need to return. */
31891  if (pSRC->config.neverConsumeEndOfInput) {
31892  if ((pSRC->sinc.inputFrameCount * inverseFactor) <= pSRC->config.sinc.windowWidth) {
31893  break;
31894  }
31895  } else {
31896  if ((pSRC->sinc.inputFrameCount * inverseFactor) < 1) {
31897  break;
31898  }
31899  }
31900  }
31901 
31902  /* Anything left over in the cache must be set to zero. */
31903  leftoverFrames = ma_countof(pSRC->sinc.input[0]) - (pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount);
31904  if (leftoverFrames > 0) {
31905  for (iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) {
31906  ma_zero_memory(pSRC->sinc.input[iChannel] + pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount, leftoverFrames * sizeof(float));
31907  }
31908  }
31909  }
31910  }
31911 
31912  return totalOutputFramesRead;
31913 }
31914 
31915 
31916 
31917 /**************************************************************************************************************************************************************
31918 
31919 Format Conversion
31920 
31921 **************************************************************************************************************************************************************/
31922 void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
31923 {
31924  if (formatOut == formatIn) {
31925  ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut));
31926  return;
31927  }
31928 
31929  switch (formatIn)
31930  {
31931  case ma_format_u8:
31932  {
31933  switch (formatOut)
31934  {
31935  case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return;
31936  case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return;
31937  case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return;
31938  case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return;
31939  default: break;
31940  }
31941  } break;
31942 
31943  case ma_format_s16:
31944  {
31945  switch (formatOut)
31946  {
31947  case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return;
31948  case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return;
31949  case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return;
31950  case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return;
31951  default: break;
31952  }
31953  } break;
31954 
31955  case ma_format_s24:
31956  {
31957  switch (formatOut)
31958  {
31959  case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return;
31960  case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return;
31961  case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return;
31962  case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return;
31963  default: break;
31964  }
31965  } break;
31966 
31967  case ma_format_s32:
31968  {
31969  switch (formatOut)
31970  {
31971  case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
31972  case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
31973  case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
31974  case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return;
31975  default: break;
31976  }
31977  } break;
31978 
31979  case ma_format_f32:
31980  {
31981  switch (formatOut)
31982  {
31983  case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
31984  case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
31985  case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
31986  case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return;
31987  default: break;
31988  }
31989  } break;
31990 
31991  default: break;
31992  }
31993 }
31994 
31995 void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
31996 {
31997  if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
31998  return; /* Invalid args. */
31999  }
32000 
32001  /* For efficiency we do this per format. */
32002  switch (format) {
32003  case ma_format_s16:
32004  {
32005  const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames;
32006  ma_uint64 iPCMFrame;
32007  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
32008  ma_uint32 iChannel;
32009  for (iChannel = 0; iChannel < channels; ++iChannel) {
32010  ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel];
32011  pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
32012  }
32013  }
32014  } break;
32015 
32016  case ma_format_f32:
32017  {
32018  const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
32019  ma_uint64 iPCMFrame;
32020  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
32021  ma_uint32 iChannel;
32022  for (iChannel = 0; iChannel < channels; ++iChannel) {
32023  float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
32024  pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
32025  }
32026  }
32027  } break;
32028 
32029  default:
32030  {
32031  ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
32032  ma_uint64 iPCMFrame;
32033  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
32034  ma_uint32 iChannel;
32035  for (iChannel = 0; iChannel < channels; ++iChannel) {
32036  void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
32037  const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
32038  memcpy(pDst, pSrc, sampleSizeInBytes);
32039  }
32040  }
32041  } break;
32042  }
32043 }
32044 
32045 void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
32046 {
32047  switch (format)
32048  {
32049  case ma_format_s16:
32050  {
32051  ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames;
32052  ma_uint64 iPCMFrame;
32053  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
32054  ma_uint32 iChannel;
32055  for (iChannel = 0; iChannel < channels; ++iChannel) {
32056  const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel];
32057  pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
32058  }
32059  }
32060  } break;
32061 
32062  case ma_format_f32:
32063  {
32064  float* pDstF32 = (float*)pInterleavedPCMFrames;
32065  ma_uint64 iPCMFrame;
32066  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
32067  ma_uint32 iChannel;
32068  for (iChannel = 0; iChannel < channels; ++iChannel) {
32069  const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
32070  pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
32071  }
32072  }
32073  } break;
32074 
32075  default:
32076  {
32077  ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
32078  ma_uint64 iPCMFrame;
32079  for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
32080  ma_uint32 iChannel;
32081  for (iChannel = 0; iChannel < channels; ++iChannel) {
32082  void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
32083  const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
32084  memcpy(pDst, pSrc, sampleSizeInBytes);
32085  }
32086  }
32087  } break;
32088  }
32089 }
32090 
32091 
32092 
32093 typedef struct
32094 {
32095  ma_pcm_converter* pDSP;
32096  void* pUserDataForClient;
32097 } ma_pcm_converter_callback_data;
32098 
32099 ma_uint32 ma_pcm_converter__pre_format_converter_on_read(ma_format_converter* pConverter, ma_uint32 frameCount, void* pFramesOut, void* pUserData)
32100 {
32101  ma_pcm_converter_callback_data* pData;
32102  ma_pcm_converter* pDSP;
32103 
32104  (void)pConverter;
32105 
32106  pData = (ma_pcm_converter_callback_data*)pUserData;
32107  ma_assert(pData != NULL);
32108 
32109  pDSP = pData->pDSP;
32110  ma_assert(pDSP != NULL);
32111 
32112  return pDSP->onRead(pDSP, pFramesOut, frameCount, pData->pUserDataForClient);
32113 }
32114 
32115 ma_uint32 ma_pcm_converter__post_format_converter_on_read(ma_format_converter* pConverter, ma_uint32 frameCount, void* pFramesOut, void* pUserData)
32116 {
32117  ma_pcm_converter_callback_data* pData;
32118  ma_pcm_converter* pDSP;
32119 
32120  (void)pConverter;
32121 
32122  pData = (ma_pcm_converter_callback_data*)pUserData;
32123  ma_assert(pData != NULL);
32124 
32125  pDSP = pData->pDSP;
32126  ma_assert(pDSP != NULL);
32127 
32128  /* When this version of this callback is used it means we're reading directly from the client. */
32129  ma_assert(pDSP->isPreFormatConversionRequired == MA_FALSE);
32130  ma_assert(pDSP->isChannelRoutingRequired == MA_FALSE);
32131  ma_assert(pDSP->isSRCRequired == MA_FALSE);
32132 
32133  return pDSP->onRead(pDSP, pFramesOut, frameCount, pData->pUserDataForClient);
32134 }
32135 
32136 ma_uint32 ma_pcm_converter__post_format_converter_on_read_deinterleaved(ma_format_converter* pConverter, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData)
32137 {
32138  ma_pcm_converter_callback_data* pData;
32139  ma_pcm_converter* pDSP;
32140 
32141  (void)pConverter;
32142 
32143  pData = (ma_pcm_converter_callback_data*)pUserData;
32144  ma_assert(pData != NULL);
32145 
32146  pDSP = pData->pDSP;
32147  ma_assert(pDSP != NULL);
32148 
32149  if (!pDSP->isChannelRoutingAtStart) {
32150  return (ma_uint32)ma_channel_router_read_deinterleaved(&pDSP->channelRouter, frameCount, ppSamplesOut, pUserData);
32151  } else {
32152  if (pDSP->isSRCRequired) {
32153  return (ma_uint32)ma_src_read_deinterleaved(&pDSP->src, frameCount, ppSamplesOut, pUserData);
32154  } else {
32155  return (ma_uint32)ma_channel_router_read_deinterleaved(&pDSP->channelRouter, frameCount, ppSamplesOut, pUserData);
32156  }
32157  }
32158 }
32159 
32160 ma_uint32 ma_pcm_converter__src_on_read_deinterleaved(ma_src* pSRC, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData)
32161 {
32162  ma_pcm_converter_callback_data* pData;
32163  ma_pcm_converter* pDSP;
32164 
32165  (void)pSRC;
32166 
32167  pData = (ma_pcm_converter_callback_data*)pUserData;
32168  ma_assert(pData != NULL);
32169 
32170  pDSP = pData->pDSP;
32171  ma_assert(pDSP != NULL);
32172 
32173  /* If the channel routing stage is at the front we need to read from that. Otherwise we read from the pre format converter. */
32174  if (pDSP->isChannelRoutingAtStart) {
32175  return (ma_uint32)ma_channel_router_read_deinterleaved(&pDSP->channelRouter, frameCount, ppSamplesOut, pUserData);
32176  } else {
32177  return (ma_uint32)ma_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData);
32178  }
32179 }
32180 
32181 ma_uint32 ma_pcm_converter__channel_router_on_read_deinterleaved(ma_channel_router* pRouter, ma_uint32 frameCount, void** ppSamplesOut, void* pUserData)
32182 {
32183  ma_pcm_converter_callback_data* pData;
32184  ma_pcm_converter* pDSP;
32185 
32186  (void)pRouter;
32187 
32188  pData = (ma_pcm_converter_callback_data*)pUserData;
32189  ma_assert(pData != NULL);
32190 
32191  pDSP = pData->pDSP;
32192  ma_assert(pDSP != NULL);
32193 
32194  /* If the channel routing stage is at the front of the pipeline we read from the pre format converter. Otherwise we read from the sample rate converter. */
32195  if (pDSP->isChannelRoutingAtStart) {
32196  return (ma_uint32)ma_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData);
32197  } else {
32198  if (pDSP->isSRCRequired) {
32199  return (ma_uint32)ma_src_read_deinterleaved(&pDSP->src, frameCount, ppSamplesOut, pUserData);
32200  } else {
32201  return (ma_uint32)ma_format_converter_read_deinterleaved(&pDSP->formatConverterIn, frameCount, ppSamplesOut, pUserData);
32202  }
32203  }
32204 }
32205 
32207 {
32208  ma_result result;
32209 
32210  if (pDSP == NULL) {
32211  return MA_INVALID_ARGS;
32212  }
32213 
32214  ma_zero_object(pDSP);
32215  pDSP->onRead = pConfig->onRead;
32216  pDSP->pUserData = pConfig->pUserData;
32217  pDSP->isDynamicSampleRateAllowed = pConfig->allowDynamicSampleRate;
32218 
32219  /*
32220  In general, this is the pipeline used for data conversion. Note that this can actually change which is explained later.
32221 
32222  Pre Format Conversion -> Sample Rate Conversion -> Channel Routing -> Post Format Conversion
32223 
32224  Pre Format Conversion
32225  ---------------------
32226  This is where the sample data is converted to a format that's usable by the later stages in the pipeline. Input data
32227  is converted to deinterleaved floating-point.
32228 
32229  Channel Routing
32230  ---------------
32231  Channel routing is where stereo is converted to 5.1, mono is converted to stereo, etc. This stage depends on the
32232  pre format conversion stage.
32233 
32234  Sample Rate Conversion
32235  ----------------------
32236  Sample rate conversion depends on the pre format conversion stage and as the name implies performs sample rate conversion.
32237 
32238  Post Format Conversion
32239  ----------------------
32240  This stage is where our deinterleaved floating-point data from the previous stages are converted to the requested output
32241  format.
32242 
32243 
32244  Optimizations
32245  -------------
32246  Sometimes the conversion pipeline is rearranged for efficiency. The first obvious optimization is to eliminate unnecessary
32247  stages in the pipeline. When no channel routing nor sample rate conversion is necessary, the entire pipeline is optimized
32248  down to just this:
32249 
32250  Post Format Conversion
32251 
32252  When sample rate conversion is not unnecessary:
32253 
32254  Pre Format Conversion -> Channel Routing -> Post Format Conversion
32255 
32256  When channel routing is unnecessary:
32257 
32258  Pre Format Conversion -> Sample Rate Conversion -> Post Format Conversion
32259 
32260  A slightly less obvious optimization is used depending on whether or not we are increasing or decreasing the number of
32261  channels. Because everything in the pipeline works on a per-channel basis, the efficiency of the pipeline is directly
32262  proportionate to the number of channels that need to be processed. Therefore, it's can be more efficient to move the
32263  channel conversion stage to an earlier or later stage. When the channel count is being reduced, we move the channel
32264  conversion stage to the start of the pipeline so that later stages can work on a smaller number of channels at a time.
32265  Otherwise, we move the channel conversion stage to the end of the pipeline. When reducing the channel count, the pipeline
32266  will look like this:
32267 
32268  Pre Format Conversion -> Channel Routing -> Sample Rate Conversion -> Post Format Conversion
32269 
32270  Notice how the Channel Routing and Sample Rate Conversion stages are swapped so that the SRC stage has less data to process.
32271  */
32272 
32273  /* First we need to determine what's required and what's not. */
32274  if (pConfig->sampleRateIn != pConfig->sampleRateOut || pConfig->allowDynamicSampleRate) {
32275  pDSP->isSRCRequired = MA_TRUE;
32276  }
32277  if (pConfig->channelsIn != pConfig->channelsOut || !ma_channel_map_equal(pConfig->channelsIn, pConfig->channelMapIn, pConfig->channelMapOut)) {
32278  pDSP->isChannelRoutingRequired = MA_TRUE;
32279  }
32280 
32281  /* If neither a sample rate conversion nor channel conversion is necessary we can skip the pre format conversion. */
32282  if (!pDSP->isSRCRequired && !pDSP->isChannelRoutingRequired) {
32283  /* We don't need a pre format conversion stage, but we may still need a post format conversion stage. */
32284  if (pConfig->formatIn != pConfig->formatOut) {
32285  pDSP->isPostFormatConversionRequired = MA_TRUE;
32286  }
32287  } else {
32288  pDSP->isPreFormatConversionRequired = MA_TRUE;
32289  pDSP->isPostFormatConversionRequired = MA_TRUE;
32290  }
32291 
32292  /* Use a passthrough if none of the stages are being used. */
32293  if (!pDSP->isPreFormatConversionRequired && !pDSP->isPostFormatConversionRequired && !pDSP->isChannelRoutingRequired && !pDSP->isSRCRequired) {
32294  pDSP->isPassthrough = MA_TRUE;
32295  }
32296 
32297  /* Move the channel conversion stage to the start of the pipeline if we are reducing the channel count. */
32298  if (pConfig->channelsOut < pConfig->channelsIn) {
32299  pDSP->isChannelRoutingAtStart = MA_TRUE;
32300  }
32301 
32302 
32303  /*
32304  We always initialize every stage of the pipeline regardless of whether or not the stage is used because it simplifies
32305  a few things when it comes to dynamically changing properties post-initialization.
32306  */
32307  result = MA_SUCCESS;
32308 
32309  /* Pre format conversion. */
32310  {
32312  pConfig->formatIn,
32313  ma_format_f32,
32314  pConfig->channelsIn,
32315  ma_pcm_converter__pre_format_converter_on_read,
32316  pDSP
32317  );
32318  preFormatConverterConfig.ditherMode = pConfig->ditherMode;
32319  preFormatConverterConfig.noSSE2 = pConfig->noSSE2;
32320  preFormatConverterConfig.noAVX2 = pConfig->noAVX2;
32321  preFormatConverterConfig.noAVX512 = pConfig->noAVX512;
32322  preFormatConverterConfig.noNEON = pConfig->noNEON;
32323 
32324  result = ma_format_converter_init(&preFormatConverterConfig, &pDSP->formatConverterIn);
32325  if (result != MA_SUCCESS) {
32326  return result;
32327  }
32328  }
32329 
32330  /*
32331  Post format conversion. The exact configuration for this depends on whether or not we are reading data directly from the client
32332  or from an earlier stage in the pipeline.
32333  */
32334  {
32336  postFormatConverterConfig.formatIn = pConfig->formatIn;
32337  postFormatConverterConfig.formatOut = pConfig->formatOut;
32338  postFormatConverterConfig.channels = pConfig->channelsOut;
32339  postFormatConverterConfig.ditherMode = pConfig->ditherMode;
32340  postFormatConverterConfig.noSSE2 = pConfig->noSSE2;
32341  postFormatConverterConfig.noAVX2 = pConfig->noAVX2;
32342  postFormatConverterConfig.noAVX512 = pConfig->noAVX512;
32343  postFormatConverterConfig.noNEON = pConfig->noNEON;
32344  if (pDSP->isPreFormatConversionRequired) {
32345  postFormatConverterConfig.onReadDeinterleaved = ma_pcm_converter__post_format_converter_on_read_deinterleaved;
32346  postFormatConverterConfig.formatIn = ma_format_f32;
32347  } else {
32348  postFormatConverterConfig.onRead = ma_pcm_converter__post_format_converter_on_read;
32349  }
32350 
32351  result = ma_format_converter_init(&postFormatConverterConfig, &pDSP->formatConverterOut);
32352  if (result != MA_SUCCESS) {
32353  return result;
32354  }
32355  }
32356 
32357  /* SRC */
32358  {
32359  ma_src_config srcConfig = ma_src_config_init(
32360  pConfig->sampleRateIn,
32361  pConfig->sampleRateOut,
32362  ((pConfig->channelsIn < pConfig->channelsOut) ? pConfig->channelsIn : pConfig->channelsOut),
32363  ma_pcm_converter__src_on_read_deinterleaved,
32364  pDSP
32365  );
32366  srcConfig.algorithm = pConfig->srcAlgorithm;
32367  srcConfig.neverConsumeEndOfInput = pConfig->neverConsumeEndOfInput;
32368  srcConfig.noSSE2 = pConfig->noSSE2;
32369  srcConfig.noAVX2 = pConfig->noAVX2;
32370  srcConfig.noAVX512 = pConfig->noAVX512;
32371  srcConfig.noNEON = pConfig->noNEON;
32372  ma_copy_memory(&srcConfig.sinc, &pConfig->sinc, sizeof(pConfig->sinc));
32373 
32374  result = ma_src_init(&srcConfig, &pDSP->src);
32375  if (result != MA_SUCCESS) {
32376  return result;
32377  }
32378  }
32379 
32380  /* Channel conversion */
32381  {
32383  pConfig->channelsIn,
32384  pConfig->channelMapIn,
32385  pConfig->channelsOut,
32386  pConfig->channelMapOut,
32387  pConfig->channelMixMode,
32388  ma_pcm_converter__channel_router_on_read_deinterleaved,
32389  pDSP);
32390  routerConfig.noSSE2 = pConfig->noSSE2;
32391  routerConfig.noAVX2 = pConfig->noAVX2;
32392  routerConfig.noAVX512 = pConfig->noAVX512;
32393  routerConfig.noNEON = pConfig->noNEON;
32394 
32395  result = ma_channel_router_init(&routerConfig, &pDSP->channelRouter);
32396  if (result != MA_SUCCESS) {
32397  return result;
32398  }
32399  }
32400 
32401  return MA_SUCCESS;
32402 }
32403 
32404 
32405 ma_result ma_pcm_converter_refresh_sample_rate(ma_pcm_converter* pDSP)
32406 {
32407  /* The SRC stage will already have been initialized so we can just set it there. */
32408  ma_src_set_sample_rate(&pDSP->src, pDSP->src.config.sampleRateIn, pDSP->src.config.sampleRateOut);
32409  return MA_SUCCESS;
32410 }
32411 
32413 {
32414  if (pDSP == NULL) {
32415  return MA_INVALID_ARGS;
32416  }
32417 
32418  /* Must have a sample rate of > 0. */
32419  if (sampleRateIn == 0) {
32420  return MA_INVALID_ARGS;
32421  }
32422 
32423  /* Must have been initialized with allowDynamicSampleRate. */
32424  if (!pDSP->isDynamicSampleRateAllowed) {
32425  return MA_INVALID_OPERATION;
32426  }
32427 
32428  ma_atomic_exchange_32(&pDSP->src.config.sampleRateIn, sampleRateIn);
32429  return ma_pcm_converter_refresh_sample_rate(pDSP);
32430 }
32431 
32433 {
32434  if (pDSP == NULL) {
32435  return MA_INVALID_ARGS;
32436  }
32437 
32438  /* Must have a sample rate of > 0. */
32439  if (sampleRateOut == 0) {
32440  return MA_INVALID_ARGS;
32441  }
32442 
32443  /* Must have been initialized with allowDynamicSampleRate. */
32444  if (!pDSP->isDynamicSampleRateAllowed) {
32445  return MA_INVALID_OPERATION;
32446  }
32447 
32448  ma_atomic_exchange_32(&pDSP->src.config.sampleRateOut, sampleRateOut);
32449  return ma_pcm_converter_refresh_sample_rate(pDSP);
32450 }
32451 
32453 {
32454  if (pDSP == NULL) {
32455  return MA_INVALID_ARGS;
32456  }
32457 
32458  /* Must have a sample rate of > 0. */
32459  if (sampleRateIn == 0 || sampleRateOut == 0) {
32460  return MA_INVALID_ARGS;
32461  }
32462 
32463  /* Must have been initialized with allowDynamicSampleRate. */
32464  if (!pDSP->isDynamicSampleRateAllowed) {
32465  return MA_INVALID_OPERATION;
32466  }
32467 
32468  ma_atomic_exchange_32(&pDSP->src.config.sampleRateIn, sampleRateIn);
32469  ma_atomic_exchange_32(&pDSP->src.config.sampleRateOut, sampleRateOut);
32470 
32471  return ma_pcm_converter_refresh_sample_rate(pDSP);
32472 }
32473 
32474 ma_uint64 ma_pcm_converter_read(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint64 frameCount)
32475 {
32476  ma_pcm_converter_callback_data data;
32477 
32478  if (pDSP == NULL || pFramesOut == NULL) {
32479  return 0;
32480  }
32481 
32482  /* Fast path. */
32483  if (pDSP->isPassthrough) {
32484  if (frameCount <= 0xFFFFFFFF) {
32485  return (ma_uint32)pDSP->onRead(pDSP, pFramesOut, (ma_uint32)frameCount, pDSP->pUserData);
32486  } else {
32487  ma_uint8* pNextFramesOut = (ma_uint8*)pFramesOut;
32488 
32489  ma_uint64 totalFramesRead = 0;
32490  while (totalFramesRead < frameCount) {
32491  ma_uint32 framesRead;
32492  ma_uint64 framesRemaining = (frameCount - totalFramesRead);
32493  ma_uint64 framesToReadRightNow = framesRemaining;
32494  if (framesToReadRightNow > 0xFFFFFFFF) {
32495  framesToReadRightNow = 0xFFFFFFFF;
32496  }
32497 
32498  framesRead = pDSP->onRead(pDSP, pNextFramesOut, (ma_uint32)framesToReadRightNow, pDSP->pUserData);
32499  if (framesRead == 0) {
32500  break;
32501  }
32502 
32503  pNextFramesOut += framesRead * pDSP->channelRouter.config.channelsOut * ma_get_bytes_per_sample(pDSP->formatConverterOut.config.formatOut);
32504  totalFramesRead += framesRead;
32505  }
32506 
32507  return totalFramesRead;
32508  }
32509  }
32510 
32511  /* Slower path. The real work is done here. To do this all we need to do is read from the last stage in the pipeline. */
32512  ma_assert(pDSP->isPostFormatConversionRequired == MA_TRUE);
32513 
32514  data.pDSP = pDSP;
32515  data.pUserDataForClient = pDSP->pUserData;
32516  return ma_format_converter_read(&pDSP->formatConverterOut, frameCount, pFramesOut, &data);
32517 }
32518 
32519 
32520 typedef struct
32521 {
32522  const void* pDataIn;
32523  ma_format formatIn;
32524  ma_uint32 channelsIn;
32525  ma_uint64 totalFrameCount;
32526  ma_uint64 iNextFrame;
32527  ma_bool32 isFeedingZeros; /* When set to true, feeds the DSP zero samples. */
32528 } ma_convert_frames__data;
32529 
32530 ma_uint32 ma_convert_frames__on_read(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint32 frameCount, void* pUserData)
32531 {
32532  ma_convert_frames__data* pData;
32533  ma_uint32 framesToRead;
32534  ma_uint64 framesRemaining;
32535  ma_uint32 frameSizeInBytes;
32536 
32537  (void)pDSP;
32538 
32539  pData = (ma_convert_frames__data*)pUserData;
32540  ma_assert(pData != NULL);
32541  ma_assert(pData->totalFrameCount >= pData->iNextFrame);
32542 
32543  framesToRead = frameCount;
32544  framesRemaining = (pData->totalFrameCount - pData->iNextFrame);
32545  if (framesToRead > framesRemaining) {
32546  framesToRead = (ma_uint32)framesRemaining;
32547  }
32548 
32549  frameSizeInBytes = ma_get_bytes_per_frame(pData->formatIn, pData->channelsIn);
32550 
32551  if (!pData->isFeedingZeros) {
32552  ma_copy_memory(pFramesOut, (const ma_uint8*)pData->pDataIn + (frameSizeInBytes * pData->iNextFrame), frameSizeInBytes * framesToRead);
32553  } else {
32554  ma_zero_memory(pFramesOut, frameSizeInBytes * framesToRead);
32555  }
32556 
32557  pData->iNextFrame += framesToRead;
32558  return framesToRead;
32559 }
32560 
32562 {
32564  ma_zero_object(&config);
32565 
32566  return config;
32567 }
32568 
32569 ma_pcm_converter_config ma_pcm_converter_config_init(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_pcm_converter_read_proc onRead, void* pUserData)
32570 {
32571  return ma_pcm_converter_config_init_ex(formatIn, channelsIn, sampleRateIn, NULL, formatOut, channelsOut, sampleRateOut, NULL, onRead, pUserData);
32572 }
32573 
32574 ma_pcm_converter_config ma_pcm_converter_config_init_ex(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_channel channelMapIn[MA_MAX_CHANNELS], ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_channel channelMapOut[MA_MAX_CHANNELS], ma_pcm_converter_read_proc onRead, void* pUserData)
32575 {
32577  ma_zero_object(&config);
32578  config.formatIn = formatIn;
32579  config.channelsIn = channelsIn;
32580  config.sampleRateIn = sampleRateIn;
32581  config.formatOut = formatOut;
32582  config.channelsOut = channelsOut;
32583  config.sampleRateOut = sampleRateOut;
32584  if (channelMapIn != NULL) {
32585  ma_copy_memory(config.channelMapIn, channelMapIn, sizeof(config.channelMapIn));
32586  }
32587  if (channelMapOut != NULL) {
32588  ma_copy_memory(config.channelMapOut, channelMapOut, sizeof(config.channelMapOut));
32589  }
32590  config.onRead = onRead;
32591  config.pUserData = pUserData;
32592 
32593  return config;
32594 }
32595 
32596 
32597 
32598 ma_uint64 ma_convert_frames(void* pOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_uint64 frameCount)
32599 {
32600  ma_channel channelMapOut[MA_MAX_CHANNELS];
32601  ma_channel channelMapIn[MA_MAX_CHANNELS];
32602 
32603  ma_get_standard_channel_map(ma_standard_channel_map_default, channelsOut, channelMapOut);
32605 
32606  return ma_convert_frames_ex(pOut, formatOut, channelsOut, sampleRateOut, channelMapOut, pIn, formatIn, channelsIn, sampleRateIn, channelMapIn, frameCount);
32607 }
32608 
32609 ma_uint64 ma_convert_frames_ex(void* pOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_channel channelMapOut[MA_MAX_CHANNELS], const void* pIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_channel channelMapIn[MA_MAX_CHANNELS], ma_uint64 frameCount)
32610 {
32611  ma_uint64 frameCountOut;
32612  ma_convert_frames__data data;
32613  ma_pcm_converter_config converterConfig;
32614  ma_pcm_converter converter;
32615  ma_uint64 totalFramesRead;
32616 
32617  if (frameCount == 0) {
32618  return 0;
32619  }
32620 
32621  frameCountOut = ma_calculate_frame_count_after_src(sampleRateOut, sampleRateIn, frameCount);
32622  if (pOut == NULL) {
32623  return frameCountOut;
32624  }
32625 
32626  data.pDataIn = pIn;
32627  data.formatIn = formatIn;
32628  data.channelsIn = channelsIn;
32629  data.totalFrameCount = frameCount;
32630  data.iNextFrame = 0;
32631  data.isFeedingZeros = MA_FALSE;
32632 
32633  ma_zero_object(&converterConfig);
32634 
32635  converterConfig.formatIn = formatIn;
32636  converterConfig.channelsIn = channelsIn;
32637  converterConfig.sampleRateIn = sampleRateIn;
32638  if (channelMapIn != NULL) {
32639  ma_channel_map_copy(converterConfig.channelMapIn, channelMapIn, channelsIn);
32640  } else {
32642  }
32643 
32644  converterConfig.formatOut = formatOut;
32645  converterConfig.channelsOut = channelsOut;
32646  converterConfig.sampleRateOut = sampleRateOut;
32647  if (channelMapOut != NULL) {
32648  ma_channel_map_copy(converterConfig.channelMapOut, channelMapOut, channelsOut);
32649  } else {
32651  }
32652 
32653  converterConfig.onRead = ma_convert_frames__on_read;
32654  converterConfig.pUserData = &data;
32655 
32656  if (ma_pcm_converter_init(&converterConfig, &converter) != MA_SUCCESS) {
32657  return 0;
32658  }
32659 
32660  /*
32661  Always output our computed frame count. There is a chance the sample rate conversion routine may not output the last sample
32662  due to precision issues with 32-bit floats, in which case we should feed the DSP zero samples so it can generate that last
32663  frame.
32664  */
32665  totalFramesRead = ma_pcm_converter_read(&converter, pOut, frameCountOut);
32666  if (totalFramesRead < frameCountOut) {
32667  ma_uint32 bpfOut = ma_get_bytes_per_frame(formatOut, channelsOut);
32668 
32669  data.isFeedingZeros = MA_TRUE;
32670  data.totalFrameCount = ((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF; /* C89 does not support 64-bit constants so need to instead construct it like this. Annoying... */ /*data.totalFrameCount = 0xFFFFFFFFFFFFFFFF;*/
32671  data.pDataIn = NULL;
32672 
32673  while (totalFramesRead < frameCountOut) {
32674  ma_uint64 framesToRead;
32675  ma_uint64 framesJustRead;
32676 
32677  framesToRead = (frameCountOut - totalFramesRead);
32678  ma_assert(framesToRead > 0);
32679 
32680  framesJustRead = ma_pcm_converter_read(&converter, ma_offset_ptr(pOut, totalFramesRead * bpfOut), framesToRead);
32681  totalFramesRead += framesJustRead;
32682 
32683  if (framesJustRead < framesToRead) {
32684  break;
32685  }
32686  }
32687 
32688  /* At this point we should have output every sample, but just to be super duper sure, just fill the rest with zeros. */
32689  if (totalFramesRead < frameCountOut) {
32690  ma_zero_memory_64(ma_offset_ptr(pOut, totalFramesRead * bpfOut), ((frameCountOut - totalFramesRead) * bpfOut));
32691  totalFramesRead = frameCountOut;
32692  }
32693  }
32694 
32695  ma_assert(totalFramesRead == frameCountOut);
32696  return totalFramesRead;
32697 }
32698 
32699 
32700 /**************************************************************************************************************************************************************
32701 
32702 Ring Buffer
32703 
32704 **************************************************************************************************************************************************************/
32705 MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset)
32706 {
32707  return encodedOffset & 0x7FFFFFFF;
32708 }
32709 
32710 MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset)
32711 {
32712  return encodedOffset & 0x80000000;
32713 }
32714 
32715 MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB)
32716 {
32717  ma_assert(pRB != NULL);
32718  return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(pRB->encodedReadOffset));
32719 }
32720 
32721 MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB)
32722 {
32723  ma_assert(pRB != NULL);
32724  return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(pRB->encodedWriteOffset));
32725 }
32726 
32727 MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag)
32728 {
32729  return offsetLoopFlag | offsetInBytes;
32730 }
32731 
32732 MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag)
32733 {
32734  ma_assert(pOffsetInBytes != NULL);
32735  ma_assert(pOffsetLoopFlag != NULL);
32736 
32737  *pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset);
32738  *pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset);
32739 }
32740 
32741 
32742 ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, ma_rb* pRB)
32743 {
32744  const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1);
32745 
32746  if (pRB == NULL) {
32747  return MA_INVALID_ARGS;
32748  }
32749 
32750  if (subbufferSizeInBytes == 0 || subbufferCount == 0) {
32751  return MA_INVALID_ARGS;
32752  }
32753 
32754  if (subbufferSizeInBytes > maxSubBufferSize) {
32755  return MA_INVALID_ARGS; /* Maximum buffer size is ~2GB. The most significant bit is a flag for use internally. */
32756  }
32757 
32758 
32759  ma_zero_object(pRB);
32760  pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes;
32761  pRB->subbufferCount = (ma_uint32)subbufferCount;
32762 
32763  if (pOptionalPreallocatedBuffer != NULL) {
32764  pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes;
32765  pRB->pBuffer = pOptionalPreallocatedBuffer;
32766  } else {
32767  size_t bufferSizeInBytes;
32768 
32769  /*
32770  Here is where we allocate our own buffer. We always want to align this to MA_SIMD_ALIGNMENT for future SIMD optimization opportunity. To do this
32771  we need to make sure the stride is a multiple of MA_SIMD_ALIGNMENT.
32772  */
32774 
32775  bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes;
32776  pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT);
32777  if (pRB->pBuffer == NULL) {
32778  return MA_OUT_OF_MEMORY;
32779  }
32780 
32781  ma_zero_memory(pRB->pBuffer, bufferSizeInBytes);
32782  pRB->ownsBuffer = MA_TRUE;
32783  }
32784 
32785  return MA_SUCCESS;
32786 }
32787 
32788 ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, ma_rb* pRB)
32789 {
32790  return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pRB);
32791 }
32792 
32793 void ma_rb_uninit(ma_rb* pRB)
32794 {
32795  if (pRB == NULL) {
32796  return;
32797  }
32798 
32799  if (pRB->ownsBuffer) {
32800  ma_aligned_free(pRB->pBuffer);
32801  }
32802 }
32803 
32804 void ma_rb_reset(ma_rb* pRB)
32805 {
32806  if (pRB == NULL) {
32807  return;
32808  }
32809 
32810  pRB->encodedReadOffset = 0;
32811  pRB->encodedWriteOffset = 0;
32812 }
32813 
32814 ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
32815 {
32817  ma_uint32 writeOffsetInBytes;
32818  ma_uint32 writeOffsetLoopFlag;
32820  ma_uint32 readOffsetInBytes;
32821  ma_uint32 readOffsetLoopFlag;
32822  size_t bytesAvailable;
32823  size_t bytesRequested;
32824 
32825  if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
32826  return MA_INVALID_ARGS;
32827  }
32828 
32829  /* The returned buffer should never move ahead of the write pointer. */
32831  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
32832 
32834  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
32835 
32836  /*
32837  The number of bytes available depends on whether or not the read and write pointers are on the same loop iteration. If so, we
32838  can only read up to the write pointer. If not, we can only read up to the end of the buffer.
32839  */
32840  if (readOffsetLoopFlag == writeOffsetLoopFlag) {
32841  bytesAvailable = writeOffsetInBytes - readOffsetInBytes;
32842  } else {
32843  bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes;
32844  }
32845 
32846  bytesRequested = *pSizeInBytes;
32847  if (bytesRequested > bytesAvailable) {
32848  bytesRequested = bytesAvailable;
32849  }
32850 
32851  *pSizeInBytes = bytesRequested;
32852  (*ppBufferOut) = ma_rb__get_read_ptr(pRB);
32853 
32854  return MA_SUCCESS;
32855 }
32856 
32857 ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut)
32858 {
32860  ma_uint32 readOffsetInBytes;
32861  ma_uint32 readOffsetLoopFlag;
32862  ma_uint32 newReadOffsetInBytes;
32863  ma_uint32 newReadOffsetLoopFlag;
32864 
32865  if (pRB == NULL) {
32866  return MA_INVALID_ARGS;
32867  }
32868 
32869  /* Validate the buffer. */
32870  if (pBufferOut != ma_rb__get_read_ptr(pRB)) {
32871  return MA_INVALID_ARGS;
32872  }
32873 
32875  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
32876 
32877  /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
32878  newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes);
32879  if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) {
32880  return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
32881  }
32882 
32883  /* Move the read pointer back to the start if necessary. */
32884  newReadOffsetLoopFlag = readOffsetLoopFlag;
32885  if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) {
32886  newReadOffsetInBytes = 0;
32887  newReadOffsetLoopFlag ^= 0x80000000;
32888  }
32889 
32890  ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes));
32891  return MA_SUCCESS;
32892 }
32893 
32894 ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
32895 {
32897  ma_uint32 readOffsetInBytes;
32898  ma_uint32 readOffsetLoopFlag;
32900  ma_uint32 writeOffsetInBytes;
32901  ma_uint32 writeOffsetLoopFlag;
32902  size_t bytesAvailable;
32903  size_t bytesRequested;
32904 
32905  if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
32906  return MA_INVALID_ARGS;
32907  }
32908 
32909  /* The returned buffer should never overtake the read buffer. */
32911  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
32912 
32914  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
32915 
32916  /*
32917  In the case of writing, if the write pointer and the read pointer are on the same loop iteration we can only
32918  write up to the end of the buffer. Otherwise we can only write up to the read pointer. The write pointer should
32919  never overtake the read pointer.
32920  */
32921  if (writeOffsetLoopFlag == readOffsetLoopFlag) {
32922  bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes;
32923  } else {
32924  bytesAvailable = readOffsetInBytes - writeOffsetInBytes;
32925  }
32926 
32927  bytesRequested = *pSizeInBytes;
32928  if (bytesRequested > bytesAvailable) {
32929  bytesRequested = bytesAvailable;
32930  }
32931 
32932  *pSizeInBytes = bytesRequested;
32933  *ppBufferOut = ma_rb__get_write_ptr(pRB);
32934 
32935  /* Clear the buffer if desired. */
32936  if (pRB->clearOnWriteAcquire) {
32937  ma_zero_memory(*ppBufferOut, *pSizeInBytes);
32938  }
32939 
32940  return MA_SUCCESS;
32941 }
32942 
32943 ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes, void* pBufferOut)
32944 {
32946  ma_uint32 writeOffsetInBytes;
32947  ma_uint32 writeOffsetLoopFlag;
32948  ma_uint32 newWriteOffsetInBytes;
32949  ma_uint32 newWriteOffsetLoopFlag;
32950 
32951  if (pRB == NULL) {
32952  return MA_INVALID_ARGS;
32953  }
32954 
32955  /* Validate the buffer. */
32956  if (pBufferOut != ma_rb__get_write_ptr(pRB)) {
32957  return MA_INVALID_ARGS;
32958  }
32959 
32961  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
32962 
32963  /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */
32964  newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes);
32965  if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) {
32966  return MA_INVALID_ARGS; /* <-- sizeInBytes will cause the read offset to overflow. */
32967  }
32968 
32969  /* Move the read pointer back to the start if necessary. */
32970  newWriteOffsetLoopFlag = writeOffsetLoopFlag;
32971  if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) {
32972  newWriteOffsetInBytes = 0;
32973  newWriteOffsetLoopFlag ^= 0x80000000;
32974  }
32975 
32976  ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes));
32977  return MA_SUCCESS;
32978 }
32979 
32980 ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes)
32981 {
32983  ma_uint32 readOffsetInBytes;
32984  ma_uint32 readOffsetLoopFlag;
32986  ma_uint32 writeOffsetInBytes;
32987  ma_uint32 writeOffsetLoopFlag;
32988  ma_uint32 newReadOffsetInBytes;
32989  ma_uint32 newReadOffsetLoopFlag;
32990 
32991  if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) {
32992  return MA_INVALID_ARGS;
32993  }
32994 
32996  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
32997 
32999  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
33000 
33001  newReadOffsetInBytes = readOffsetInBytes;
33002  newReadOffsetLoopFlag = readOffsetLoopFlag;
33003 
33004  /* We cannot go past the write buffer. */
33005  if (readOffsetLoopFlag == writeOffsetLoopFlag) {
33006  if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) {
33007  newReadOffsetInBytes = writeOffsetInBytes;
33008  } else {
33009  newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
33010  }
33011  } else {
33012  /* May end up looping. */
33013  if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
33014  newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
33015  newReadOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
33016  } else {
33017  newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
33018  }
33019  }
33020 
33021  ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag));
33022  return MA_SUCCESS;
33023 }
33024 
33025 ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes)
33026 {
33028  ma_uint32 readOffsetInBytes;
33029  ma_uint32 readOffsetLoopFlag;
33031  ma_uint32 writeOffsetInBytes;
33032  ma_uint32 writeOffsetLoopFlag;
33033  ma_uint32 newWriteOffsetInBytes;
33034  ma_uint32 newWriteOffsetLoopFlag;
33035 
33036  if (pRB == NULL) {
33037  return MA_INVALID_ARGS;
33038  }
33039 
33041  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
33042 
33044  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
33045 
33046  newWriteOffsetInBytes = writeOffsetInBytes;
33047  newWriteOffsetLoopFlag = writeOffsetLoopFlag;
33048 
33049  /* We cannot go past the write buffer. */
33050  if (readOffsetLoopFlag == writeOffsetLoopFlag) {
33051  /* May end up looping. */
33052  if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
33053  newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
33054  newWriteOffsetLoopFlag ^= 0x80000000; /* <-- Looped. */
33055  } else {
33056  newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
33057  }
33058  } else {
33059  if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) {
33060  newWriteOffsetInBytes = readOffsetInBytes;
33061  } else {
33062  newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
33063  }
33064  }
33065 
33066  ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag));
33067  return MA_SUCCESS;
33068 }
33069 
33071 {
33073  ma_uint32 readOffsetInBytes;
33074  ma_uint32 readOffsetLoopFlag;
33076  ma_uint32 writeOffsetInBytes;
33077  ma_uint32 writeOffsetLoopFlag;
33078 
33079  if (pRB == NULL) {
33080  return 0;
33081  }
33082 
33084  ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
33085 
33087  ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
33088 
33089  if (readOffsetLoopFlag == writeOffsetLoopFlag) {
33090  return writeOffsetInBytes - readOffsetInBytes;
33091  } else {
33092  return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes);
33093  }
33094 }
33095 
33097 {
33098  ma_int32 dist;
33099 
33100  if (pRB == NULL) {
33101  return 0;
33102  }
33103 
33104  dist = ma_rb_pointer_distance(pRB);
33105  if (dist < 0) {
33106  return 0;
33107  }
33108 
33109  return dist;
33110 }
33111 
33113 {
33114  if (pRB == NULL) {
33115  return 0;
33116  }
33117 
33119 }
33120 
33121 size_t ma_rb_get_subbuffer_size(ma_rb* pRB)
33122 {
33123  if (pRB == NULL) {
33124  return 0;
33125  }
33126 
33127  return pRB->subbufferSizeInBytes;
33128 }
33129 
33130 size_t ma_rb_get_subbuffer_stride(ma_rb* pRB)
33131 {
33132  if (pRB == NULL) {
33133  return 0;
33134  }
33135 
33136  if (pRB->subbufferStrideInBytes == 0) {
33137  return (size_t)pRB->subbufferSizeInBytes;
33138  }
33139 
33140  return (size_t)pRB->subbufferStrideInBytes;
33141 }
33142 
33143 size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex)
33144 {
33145  if (pRB == NULL) {
33146  return 0;
33147  }
33148 
33149  return subbufferIndex * ma_rb_get_subbuffer_stride(pRB);
33150 }
33151 
33152 void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer)
33153 {
33154  if (pRB == NULL) {
33155  return NULL;
33156  }
33157 
33158  return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex));
33159 }
33160 
33161 
33162 static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB)
33163 {
33164  ma_assert(pRB != NULL);
33165 
33166  return ma_get_bytes_per_frame(pRB->format, pRB->channels);
33167 }
33168 
33169 ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, ma_pcm_rb* pRB)
33170 {
33171  ma_uint32 bpf;
33172  ma_result result;
33173 
33174  if (pRB == NULL) {
33175  return MA_INVALID_ARGS;
33176  }
33177 
33178  ma_zero_object(pRB);
33179 
33180  bpf = ma_get_bytes_per_frame(format, channels);
33181  if (bpf == 0) {
33182  return MA_INVALID_ARGS;
33183  }
33184 
33185  result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, &pRB->rb);
33186  if (result != MA_SUCCESS) {
33187  return result;
33188  }
33189 
33190  pRB->format = format;
33191  pRB->channels = channels;
33192 
33193  return MA_SUCCESS;
33194 }
33195 
33196 ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, ma_pcm_rb* pRB)
33197 {
33198  return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pRB);
33199 }
33200 
33201 void ma_pcm_rb_uninit(ma_pcm_rb* pRB)
33202 {
33203  if (pRB == NULL) {
33204  return;
33205  }
33206 
33207  ma_rb_uninit(&pRB->rb);
33208 }
33209 
33210 void ma_pcm_rb_reset(ma_pcm_rb* pRB)
33211 {
33212  if (pRB == NULL) {
33213  return;
33214  }
33215 
33216  ma_rb_reset(&pRB->rb);
33217 }
33218 
33219 ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
33220 {
33221  size_t sizeInBytes;
33222  ma_result result;
33223 
33224  if (pRB == NULL || pSizeInFrames == NULL) {
33225  return MA_INVALID_ARGS;
33226  }
33227 
33228  sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
33229 
33230  result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut);
33231  if (result != MA_SUCCESS) {
33232  return result;
33233  }
33234 
33235  *pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB));
33236  return MA_SUCCESS;
33237 }
33238 
33239 ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut)
33240 {
33241  if (pRB == NULL) {
33242  return MA_INVALID_ARGS;
33243  }
33244 
33245  return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB), pBufferOut);
33246 }
33247 
33248 ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
33249 {
33250  size_t sizeInBytes;
33251  ma_result result;
33252 
33253  if (pRB == NULL) {
33254  return MA_INVALID_ARGS;
33255  }
33256 
33257  sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
33258 
33259  result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut);
33260  if (result != MA_SUCCESS) {
33261  return result;
33262  }
33263 
33264  *pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB));
33265  return MA_SUCCESS;
33266 }
33267 
33268 ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames, void* pBufferOut)
33269 {
33270  if (pRB == NULL) {
33271  return MA_INVALID_ARGS;
33272  }
33273 
33274  return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB), pBufferOut);
33275 }
33276 
33277 ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
33278 {
33279  if (pRB == NULL) {
33280  return MA_INVALID_ARGS;
33281  }
33282 
33283  return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
33284 }
33285 
33286 ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
33287 {
33288  if (pRB == NULL) {
33289  return MA_INVALID_ARGS;
33290  }
33291 
33292  return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
33293 }
33294 
33296 {
33297  if (pRB == NULL) {
33298  return 0;
33299  }
33300 
33301  return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
33302 }
33303 
33305 {
33306  if (pRB == NULL) {
33307  return 0;
33308  }
33309 
33310  return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
33311 }
33312 
33314 {
33315  if (pRB == NULL) {
33316  return 0;
33317  }
33318 
33319  return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
33320 }
33321 
33323 {
33324  if (pRB == NULL) {
33325  return 0;
33326  }
33327 
33328  return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
33329 }
33330 
33332 {
33333  if (pRB == NULL) {
33334  return 0;
33335  }
33336 
33337  return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
33338 }
33339 
33341 {
33342  if (pRB == NULL) {
33343  return 0;
33344  }
33345 
33346  return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB));
33347 }
33348 
33349 void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer)
33350 {
33351  if (pRB == NULL) {
33352  return NULL;
33353  }
33354 
33355  return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer);
33356 }
33357 
33358 
33359 
33360 /**************************************************************************************************************************************************************
33361 
33362 Miscellaneous Helpers
33363 
33364 **************************************************************************************************************************************************************/
33365 void* ma_malloc(size_t sz)
33366 {
33367  return MA_MALLOC(sz);
33368 }
33369 
33370 void* ma_realloc(void* p, size_t sz)
33371 {
33372  return MA_REALLOC(p, sz);
33373 }
33374 
33375 void ma_free(void* p)
33376 {
33377  MA_FREE(p);
33378 }
33379 
33380 void* ma_aligned_malloc(size_t sz, size_t alignment)
33381 {
33382  size_t extraBytes;
33383  void* pUnaligned;
33384  void* pAligned;
33385 
33386  if (alignment == 0) {
33387  return 0;
33388  }
33389 
33390  extraBytes = alignment-1 + sizeof(void*);
33391 
33392  pUnaligned = ma_malloc(sz + extraBytes);
33393  if (pUnaligned == NULL) {
33394  return NULL;
33395  }
33396 
33397  pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1)));
33398  ((void**)pAligned)[-1] = pUnaligned;
33399 
33400  return pAligned;
33401 }
33402 
33403 void ma_aligned_free(void* p)
33404 {
33405  ma_free(((void**)p)[-1]);
33406 }
33407 
33408 const char* ma_get_format_name(ma_format format)
33409 {
33410  switch (format)
33411  {
33412  case ma_format_unknown: return "Unknown";
33413  case ma_format_u8: return "8-bit Unsigned Integer";
33414  case ma_format_s16: return "16-bit Signed Integer";
33415  case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)";
33416  case ma_format_s32: return "32-bit Signed Integer";
33417  case ma_format_f32: return "32-bit IEEE Floating Point";
33418  default: return "Invalid";
33419  }
33420 }
33421 
33422 void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels)
33423 {
33424  ma_uint32 i;
33425  for (i = 0; i < channels; ++i) {
33426  pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor);
33427  }
33428 }
33429 
33430 
33432 {
33433  ma_uint32 sizes[] = {
33434  0, /* unknown */
33435  1, /* u8 */
33436  2, /* s16 */
33437  3, /* s24 */
33438  4, /* s32 */
33439  4, /* f32 */
33440  };
33441  return sizes[format];
33442 }
33443 
33444 
33445 /**************************************************************************************************************************************************************
33446 
33447 Decoding
33448 
33449 **************************************************************************************************************************************************************/
33450 #ifndef MA_NO_DECODING
33451 
33452 size_t ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
33453 {
33454  size_t bytesRead;
33455 
33456  ma_assert(pDecoder != NULL);
33457  ma_assert(pBufferOut != NULL);
33458 
33459  bytesRead = pDecoder->onRead(pDecoder, pBufferOut, bytesToRead);
33460  pDecoder->readPointer += bytesRead;
33461 
33462  return bytesRead;
33463 }
33464 
33465 ma_bool32 ma_decoder_seek_bytes(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin)
33466 {
33467  ma_bool32 wasSuccessful;
33468 
33469  ma_assert(pDecoder != NULL);
33470 
33471  wasSuccessful = pDecoder->onSeek(pDecoder, byteOffset, origin);
33472  if (wasSuccessful) {
33473  if (origin == ma_seek_origin_start) {
33474  pDecoder->readPointer = (ma_uint64)byteOffset;
33475  } else {
33476  pDecoder->readPointer += byteOffset;
33477  }
33478  }
33479 
33480  return wasSuccessful;
33481 }
33482 
33483 ma_bool32 ma_decoder_seek_bytes_64(ma_decoder* pDecoder, ma_uint64 byteOffset, ma_seek_origin origin)
33484 {
33485  ma_assert(pDecoder != NULL);
33486 
33487  if (origin == ma_seek_origin_start) {
33488  ma_uint64 bytesToSeekThisIteration = 0x7FFFFFFF;
33489  if (bytesToSeekThisIteration > byteOffset) {
33490  bytesToSeekThisIteration = byteOffset;
33491  }
33492 
33493  if (!ma_decoder_seek_bytes(pDecoder, (int)bytesToSeekThisIteration, ma_seek_origin_start)) {
33494  return MA_FALSE;
33495  }
33496 
33497  byteOffset -= bytesToSeekThisIteration;
33498  }
33499 
33500  /* Getting here means we need to seek relative to the current position. */
33501  while (byteOffset > 0) {
33502  ma_uint64 bytesToSeekThisIteration = 0x7FFFFFFF;
33503  if (bytesToSeekThisIteration > byteOffset) {
33504  bytesToSeekThisIteration = byteOffset;
33505  }
33506 
33507  if (!ma_decoder_seek_bytes(pDecoder, (int)bytesToSeekThisIteration, ma_seek_origin_current)) {
33508  return MA_FALSE;
33509  }
33510 
33511  byteOffset -= bytesToSeekThisIteration;
33512  }
33513 
33514  return MA_TRUE;
33515 }
33516 
33517 
33518 ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
33519 {
33521  ma_zero_object(&config);
33522  config.format = outputFormat;
33523  config.channels = outputChannels;
33524  config.sampleRate = outputSampleRate;
33526 
33527  return config;
33528 }
33529 
33530 ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig)
33531 {
33533  if (pConfig != NULL) {
33534  config = *pConfig;
33535  } else {
33536  ma_zero_object(&config);
33537  }
33538 
33539  return config;
33540 }
33541 
33542 ma_result ma_decoder__init_dsp(ma_decoder* pDecoder, const ma_decoder_config* pConfig, ma_pcm_converter_read_proc onRead)
33543 {
33544  ma_pcm_converter_config dspConfig;
33545 
33546  ma_assert(pDecoder != NULL);
33547 
33548  /* Output format. */
33549  if (pConfig->format == ma_format_unknown) {
33550  pDecoder->outputFormat = pDecoder->internalFormat;
33551  } else {
33552  pDecoder->outputFormat = pConfig->format;
33553  }
33554 
33555  if (pConfig->channels == 0) {
33556  pDecoder->outputChannels = pDecoder->internalChannels;
33557  } else {
33558  pDecoder->outputChannels = pConfig->channels;
33559  }
33560 
33561  if (pConfig->sampleRate == 0) {
33562  pDecoder->outputSampleRate = pDecoder->internalSampleRate;
33563  } else {
33564  pDecoder->outputSampleRate = pConfig->sampleRate;
33565  }
33566 
33567  if (ma_channel_map_blank(pDecoder->outputChannels, pConfig->channelMap)) {
33569  } else {
33570  ma_copy_memory(pDecoder->outputChannelMap, pConfig->channelMap, sizeof(pConfig->channelMap));
33571  }
33572 
33573 
33574  /* DSP. */
33575  dspConfig = ma_pcm_converter_config_init_ex(
33576  pDecoder->internalFormat, pDecoder->internalChannels, pDecoder->internalSampleRate, pDecoder->internalChannelMap,
33577  pDecoder->outputFormat, pDecoder->outputChannels, pDecoder->outputSampleRate, pDecoder->outputChannelMap,
33578  onRead, pDecoder);
33579  dspConfig.channelMixMode = pConfig->channelMixMode;
33580  dspConfig.ditherMode = pConfig->ditherMode;
33581  dspConfig.srcAlgorithm = pConfig->srcAlgorithm;
33582  dspConfig.sinc = pConfig->src.sinc;
33583 
33584  return ma_pcm_converter_init(&dspConfig, &pDecoder->dsp);
33585 }
33586 
33587 /* WAV */
33588 #ifdef dr_wav_h
33589 #define MA_HAS_WAV
33590 
33591 size_t ma_decoder_internal_on_read__wav(void* pUserData, void* pBufferOut, size_t bytesToRead)
33592 {
33593  ma_decoder* pDecoder = (ma_decoder*)pUserData;
33594  ma_assert(pDecoder != NULL);
33595 
33596  return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
33597 }
33598 
33599 drwav_bool32 ma_decoder_internal_on_seek__wav(void* pUserData, int offset, drwav_seek_origin origin)
33600 {
33601  ma_decoder* pDecoder = (ma_decoder*)pUserData;
33602  ma_assert(pDecoder != NULL);
33603 
33604  return ma_decoder_seek_bytes(pDecoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
33605 }
33606 
33607 ma_uint32 ma_decoder_internal_on_read_pcm_frames__wav(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint32 frameCount, void* pUserData)
33608 {
33609  ma_decoder* pDecoder;
33610  drwav* pWav;
33611 
33612  (void)pDSP;
33613 
33614  pDecoder = (ma_decoder*)pUserData;
33615  ma_assert(pDecoder != NULL);
33616 
33617  pWav = (drwav*)pDecoder->pInternalDecoder;
33618  ma_assert(pWav != NULL);
33619 
33620  switch (pDecoder->internalFormat) {
33621  case ma_format_s16: return (ma_uint32)drwav_read_pcm_frames_s16(pWav, frameCount, (drwav_int16*)pFramesOut);
33622  case ma_format_s32: return (ma_uint32)drwav_read_pcm_frames_s32(pWav, frameCount, (drwav_int32*)pFramesOut);
33623  case ma_format_f32: return (ma_uint32)drwav_read_pcm_frames_f32(pWav, frameCount, (float*)pFramesOut);
33624  default: break;
33625  }
33626 
33627  /* Should never get here. If we do, it means the internal format was not set correctly at initialization time. */
33628  ma_assert(MA_FALSE);
33629  return 0;
33630 }
33631 
33632 ma_result ma_decoder_internal_on_seek_to_pcm_frame__wav(ma_decoder* pDecoder, ma_uint64 frameIndex)
33633 {
33634  drwav* pWav;
33636 
33637  pWav = (drwav*)pDecoder->pInternalDecoder;
33638  ma_assert(pWav != NULL);
33639 
33640  result = drwav_seek_to_pcm_frame(pWav, frameIndex);
33641  if (result) {
33642  return MA_SUCCESS;
33643  } else {
33644  return MA_ERROR;
33645  }
33646 }
33647 
33648 ma_result ma_decoder_internal_on_uninit__wav(ma_decoder* pDecoder)
33649 {
33650  drwav_uninit((drwav*)pDecoder->pInternalDecoder);
33651  ma_free(pDecoder->pInternalDecoder);
33652  return MA_SUCCESS;
33653 }
33654 
33655 ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__wav(ma_decoder* pDecoder)
33656 {
33657  return ((drwav*)pDecoder->pInternalDecoder)->totalPCMFrameCount;
33658 }
33659 
33660 ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
33661 {
33662  drwav* pWav;
33663  ma_result result;
33664 
33665  ma_assert(pConfig != NULL);
33666  ma_assert(pDecoder != NULL);
33667 
33668  pWav = (drwav*)ma_malloc(sizeof(*pWav));
33669  if (pWav == NULL) {
33670  return MA_OUT_OF_MEMORY;
33671  }
33672 
33673  /* Try opening the decoder first. */
33674  if (!drwav_init(pWav, ma_decoder_internal_on_read__wav, ma_decoder_internal_on_seek__wav, pDecoder, NULL)) {
33675  ma_free(pWav);
33676  return MA_ERROR;
33677  }
33678 
33679  /* If we get here it means we successfully initialized the WAV decoder. We can now initialize the rest of the ma_decoder. */
33680  pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__wav;
33681  pDecoder->onUninit = ma_decoder_internal_on_uninit__wav;
33682  pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__wav;
33683  pDecoder->pInternalDecoder = pWav;
33684 
33685  /* Try to be as optimal as possible for the internal format. If miniaudio does not support a format we will fall back to f32. */
33686  pDecoder->internalFormat = ma_format_unknown;
33687  switch (pWav->translatedFormatTag) {
33688  case DR_WAVE_FORMAT_PCM:
33689  {
33690  if (pWav->bitsPerSample == 8) {
33691  pDecoder->internalFormat = ma_format_s16;
33692  } else if (pWav->bitsPerSample == 16) {
33693  pDecoder->internalFormat = ma_format_s16;
33694  } else if (pWav->bitsPerSample == 32) {
33695  pDecoder->internalFormat = ma_format_s32;
33696  }
33697  } break;
33698 
33700  {
33701  if (pWav->bitsPerSample == 32) {
33702  pDecoder->internalFormat = ma_format_f32;
33703  }
33704  } break;
33705 
33706  case DR_WAVE_FORMAT_ALAW:
33707  case DR_WAVE_FORMAT_MULAW:
33708  case DR_WAVE_FORMAT_ADPCM:
33710  {
33711  pDecoder->internalFormat = ma_format_s16;
33712  } break;
33713  }
33714 
33715  if (pDecoder->internalFormat == ma_format_unknown) {
33716  pDecoder->internalFormat = ma_format_f32;
33717  }
33718 
33719  pDecoder->internalChannels = pWav->channels;
33720  pDecoder->internalSampleRate = pWav->sampleRate;
33722 
33723  result = ma_decoder__init_dsp(pDecoder, pConfig, ma_decoder_internal_on_read_pcm_frames__wav);
33724  if (result != MA_SUCCESS) {
33725  drwav_uninit(pWav);
33726  ma_free(pWav);
33727  return result;
33728  }
33729 
33730  return MA_SUCCESS;
33731 }
33732 #endif
33733 
33734 /* FLAC */
33735 #ifdef dr_flac_h
33736 #define MA_HAS_FLAC
33737 
33738 size_t ma_decoder_internal_on_read__flac(void* pUserData, void* pBufferOut, size_t bytesToRead)
33739 {
33740  ma_decoder* pDecoder = (ma_decoder*)pUserData;
33741  ma_assert(pDecoder != NULL);
33742 
33743  return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
33744 }
33745 
33746 drflac_bool32 ma_decoder_internal_on_seek__flac(void* pUserData, int offset, drflac_seek_origin origin)
33747 {
33748  ma_decoder* pDecoder = (ma_decoder*)pUserData;
33749  ma_assert(pDecoder != NULL);
33750 
33751  return ma_decoder_seek_bytes(pDecoder, offset, (origin == drflac_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
33752 }
33753 
33754 ma_uint32 ma_decoder_internal_on_read_pcm_frames__flac(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint32 frameCount, void* pUserData)
33755 {
33756  ma_decoder* pDecoder;
33757  drflac* pFlac;
33758 
33759  (void)pDSP;
33760 
33761  pDecoder = (ma_decoder*)pUserData;
33762  ma_assert(pDecoder != NULL);
33763 
33764  pFlac = (drflac*)pDecoder->pInternalDecoder;
33765  ma_assert(pFlac != NULL);
33766 
33767  switch (pDecoder->internalFormat) {
33768  case ma_format_s16: return (ma_uint32)drflac_read_pcm_frames_s16(pFlac, frameCount, (drflac_int16*)pFramesOut);
33769  case ma_format_s32: return (ma_uint32)drflac_read_pcm_frames_s32(pFlac, frameCount, (drflac_int32*)pFramesOut);
33770  case ma_format_f32: return (ma_uint32)drflac_read_pcm_frames_f32(pFlac, frameCount, (float*)pFramesOut);
33771  default: break;
33772  }
33773 
33774  /* Should never get here. If we do, it means the internal format was not set correctly at initialization time. */
33775  ma_assert(MA_FALSE);
33776  return 0;
33777 }
33778 
33779 ma_result ma_decoder_internal_on_seek_to_pcm_frame__flac(ma_decoder* pDecoder, ma_uint64 frameIndex)
33780 {
33781  drflac* pFlac;
33783 
33784  pFlac = (drflac*)pDecoder->pInternalDecoder;
33785  ma_assert(pFlac != NULL);
33786 
33787  result = drflac_seek_to_pcm_frame(pFlac, frameIndex);
33788  if (result) {
33789  return MA_SUCCESS;
33790  } else {
33791  return MA_ERROR;
33792  }
33793 }
33794 
33795 ma_result ma_decoder_internal_on_uninit__flac(ma_decoder* pDecoder)
33796 {
33797  drflac_close((drflac*)pDecoder->pInternalDecoder);
33798  return MA_SUCCESS;
33799 }
33800 
33801 ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__flac(ma_decoder* pDecoder)
33802 {
33803  return ((drflac*)pDecoder->pInternalDecoder)->totalPCMFrameCount;
33804 }
33805 
33806 ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
33807 {
33808  drflac* pFlac;
33809  ma_result result;
33810 
33811  ma_assert(pConfig != NULL);
33812  ma_assert(pDecoder != NULL);
33813 
33814  /* Try opening the decoder first. */
33815  pFlac = drflac_open(ma_decoder_internal_on_read__flac, ma_decoder_internal_on_seek__flac, pDecoder, NULL);
33816  if (pFlac == NULL) {
33817  return MA_ERROR;
33818  }
33819 
33820  /* If we get here it means we successfully initialized the FLAC decoder. We can now initialize the rest of the ma_decoder. */
33821  pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__flac;
33822  pDecoder->onUninit = ma_decoder_internal_on_uninit__flac;
33823  pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__flac;
33824  pDecoder->pInternalDecoder = pFlac;
33825 
33826  /*
33827  dr_flac supports reading as s32, s16 and f32. Try to do a one-to-one mapping if possible, but fall back to s32 if not. s32 is the "native" FLAC format
33828  since it's the only one that's truly lossless.
33829  */
33830  pDecoder->internalFormat = ma_format_s32;
33831  if (pConfig->format == ma_format_s16) {
33832  pDecoder->internalFormat = ma_format_s16;
33833  } else if (pConfig->format == ma_format_f32) {
33834  pDecoder->internalFormat = ma_format_f32;
33835  }
33836 
33837  pDecoder->internalChannels = pFlac->channels;
33838  pDecoder->internalSampleRate = pFlac->sampleRate;
33840 
33841  result = ma_decoder__init_dsp(pDecoder, pConfig, ma_decoder_internal_on_read_pcm_frames__flac);
33842  if (result != MA_SUCCESS) {
33843  drflac_close(pFlac);
33844  return result;
33845  }
33846 
33847  return MA_SUCCESS;
33848 }
33849 #endif
33850 
33851 /* Vorbis */
33852 #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
33853 #define MA_HAS_VORBIS
33854 
33855 /* The size in bytes of each chunk of data to read from the Vorbis stream. */
33856 #define MA_VORBIS_DATA_CHUNK_SIZE 4096
33857 
33858 typedef struct
33859 {
33860  stb_vorbis* pInternalVorbis;
33861  ma_uint8* pData;
33862  size_t dataSize;
33863  size_t dataCapacity;
33864  ma_uint32 framesConsumed; /* The number of frames consumed in ppPacketData. */
33865  ma_uint32 framesRemaining; /* The number of frames remaining in ppPacketData. */
33866  float** ppPacketData;
33867 } ma_vorbis_decoder;
33868 
33869 ma_uint32 ma_vorbis_decoder_read_pcm_frames(ma_vorbis_decoder* pVorbis, ma_decoder* pDecoder, void* pFramesOut, ma_uint32 frameCount)
33870 {
33871  float* pFramesOutF;
33872  ma_uint32 totalFramesRead;
33873 
33874  ma_assert(pVorbis != NULL);
33875  ma_assert(pDecoder != NULL);
33876 
33877  pFramesOutF = (float*)pFramesOut;
33878 
33879  totalFramesRead = 0;
33880  while (frameCount > 0) {
33881  /* Read from the in-memory buffer first. */
33882  while (pVorbis->framesRemaining > 0 && frameCount > 0) {
33883  ma_uint32 iChannel;
33884  for (iChannel = 0; iChannel < pDecoder->internalChannels; ++iChannel) {
33885  pFramesOutF[0] = pVorbis->ppPacketData[iChannel][pVorbis->framesConsumed];
33886  pFramesOutF += 1;
33887  }
33888 
33889  pVorbis->framesConsumed += 1;
33890  pVorbis->framesRemaining -= 1;
33891  frameCount -= 1;
33892  totalFramesRead += 1;
33893  }
33894 
33895  if (frameCount == 0) {
33896  break;
33897  }
33898 
33899  ma_assert(pVorbis->framesRemaining == 0);
33900 
33901  /* We've run out of cached frames, so decode the next packet and continue iteration. */
33902  do
33903  {
33904  int samplesRead;
33905  int consumedDataSize;
33906 
33907  if (pVorbis->dataSize > INT_MAX) {
33908  break; /* Too big. */
33909  }
33910 
33911  samplesRead = 0;
33912  consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->pInternalVorbis, pVorbis->pData, (int)pVorbis->dataSize, NULL, (float***)&pVorbis->ppPacketData, &samplesRead);
33913  if (consumedDataSize != 0) {
33914  size_t leftoverDataSize = (pVorbis->dataSize - (size_t)consumedDataSize);
33915  size_t i;
33916  for (i = 0; i < leftoverDataSize; ++i) {
33917  pVorbis->pData[i] = pVorbis->pData[i + consumedDataSize];
33918  }
33919 
33920  pVorbis->dataSize = leftoverDataSize;
33921  pVorbis->framesConsumed = 0;
33922  pVorbis->framesRemaining = samplesRead;
33923  break;
33924  } else {
33925  /* Need more data. If there's any room in the existing buffer allocation fill that first. Otherwise expand. */
33926  size_t bytesRead;
33927  if (pVorbis->dataCapacity == pVorbis->dataSize) {
33928  /* No room. Expand. */
33929  size_t newCap = pVorbis->dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE;
33930  ma_uint8* pNewData;
33931 
33932  pNewData = (ma_uint8*)ma_realloc(pVorbis->pData, newCap);
33933  if (pNewData == NULL) {
33934  return totalFramesRead; /* Out of memory. */
33935  }
33936 
33937  pVorbis->pData = pNewData;
33938  pVorbis->dataCapacity = newCap;
33939  }
33940 
33941  /* Fill in a chunk. */
33942  bytesRead = ma_decoder_read_bytes(pDecoder, pVorbis->pData + pVorbis->dataSize, (pVorbis->dataCapacity - pVorbis->dataSize));
33943  if (bytesRead == 0) {
33944  return totalFramesRead; /* Error reading more data. */
33945  }
33946 
33947  pVorbis->dataSize += bytesRead;
33948  }
33949  } while (MA_TRUE);
33950  }
33951 
33952  return totalFramesRead;
33953 }
33954 
33955 ma_result ma_vorbis_decoder_seek_to_pcm_frame(ma_vorbis_decoder* pVorbis, ma_decoder* pDecoder, ma_uint64 frameIndex)
33956 {
33957  float buffer[4096];
33958 
33959  ma_assert(pVorbis != NULL);
33960  ma_assert(pDecoder != NULL);
33961 
33962  /*
33963  This is terribly inefficient because stb_vorbis does not have a good seeking solution with it's push API. Currently this just performs
33964  a full decode right from the start of the stream. Later on I'll need to write a layer that goes through all of the Ogg pages until we
33965  find the one containing the sample we need. Then we know exactly where to seek for stb_vorbis.
33966  */
33967  if (!ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start)) {
33968  return MA_ERROR;
33969  }
33970 
33971  stb_vorbis_flush_pushdata(pVorbis->pInternalVorbis);
33972  pVorbis->framesConsumed = 0;
33973  pVorbis->framesRemaining = 0;
33974  pVorbis->dataSize = 0;
33975 
33976  while (frameIndex > 0) {
33977  ma_uint32 framesRead;
33978  ma_uint32 framesToRead = ma_countof(buffer)/pDecoder->internalChannels;
33979  if (framesToRead > frameIndex) {
33980  framesToRead = (ma_uint32)frameIndex;
33981  }
33982 
33983  framesRead = ma_vorbis_decoder_read_pcm_frames(pVorbis, pDecoder, buffer, framesToRead);
33984  if (framesRead == 0) {
33985  return MA_ERROR;
33986  }
33987 
33988  frameIndex -= framesRead;
33989  }
33990 
33991  return MA_SUCCESS;
33992 }
33993 
33994 
33995 ma_result ma_decoder_internal_on_seek_to_pcm_frame__vorbis(ma_decoder* pDecoder, ma_uint64 frameIndex)
33996 {
33997  ma_vorbis_decoder* pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
33998  ma_assert(pVorbis != NULL);
33999 
34000  return ma_vorbis_decoder_seek_to_pcm_frame(pVorbis, pDecoder, frameIndex);
34001 }
34002 
34003 ma_result ma_decoder_internal_on_uninit__vorbis(ma_decoder* pDecoder)
34004 {
34005  ma_vorbis_decoder* pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
34006  ma_assert(pVorbis != NULL);
34007 
34008  stb_vorbis_close(pVorbis->pInternalVorbis);
34009  ma_free(pVorbis->pData);
34010  ma_free(pVorbis);
34011 
34012  return MA_SUCCESS;
34013 }
34014 
34015 ma_uint32 ma_decoder_internal_on_read_pcm_frames__vorbis(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint32 frameCount, void* pUserData)
34016 {
34017  ma_decoder* pDecoder;
34018  ma_vorbis_decoder* pVorbis;
34019 
34020  (void)pDSP;
34021 
34022  pDecoder = (ma_decoder*)pUserData;
34023  ma_assert(pDecoder != NULL);
34024  ma_assert(pDecoder->internalFormat == ma_format_f32);
34025 
34026  pVorbis = (ma_vorbis_decoder*)pDecoder->pInternalDecoder;
34027  ma_assert(pVorbis != NULL);
34028 
34029  return ma_vorbis_decoder_read_pcm_frames(pVorbis, pDecoder, pFramesOut, frameCount);
34030 }
34031 
34032 ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__vorbis(ma_decoder* pDecoder)
34033 {
34034  /* No good way to do this with Vorbis. */
34035  (void)pDecoder;
34036  return 0;
34037 }
34038 
34039 ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34040 {
34041  ma_result result;
34042  stb_vorbis* pInternalVorbis = NULL;
34043  size_t dataSize = 0;
34044  size_t dataCapacity = 0;
34045  ma_uint8* pData = NULL;
34046  stb_vorbis_info vorbisInfo;
34047  size_t vorbisDataSize;
34048  ma_vorbis_decoder* pVorbis;
34049 
34050  ma_assert(pConfig != NULL);
34051  ma_assert(pDecoder != NULL);
34052 
34053  /* We grow the buffer in chunks. */
34054  do
34055  {
34056  /* Allocate memory for a new chunk. */
34057  ma_uint8* pNewData;
34058  size_t bytesRead;
34059  int vorbisError = 0;
34060  int consumedDataSize = 0;
34061 
34062  dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE;
34063  pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity);
34064  if (pNewData == NULL) {
34065  ma_free(pData);
34066  return MA_OUT_OF_MEMORY;
34067  }
34068 
34069  pData = pNewData;
34070 
34071  /* Fill in a chunk. */
34072  bytesRead = ma_decoder_read_bytes(pDecoder, pData + dataSize, (dataCapacity - dataSize));
34073  if (bytesRead == 0) {
34074  return MA_ERROR;
34075  }
34076 
34077  dataSize += bytesRead;
34078  if (dataSize > INT_MAX) {
34079  return MA_ERROR; /* Too big. */
34080  }
34081 
34082  pInternalVorbis = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL);
34083  if (pInternalVorbis != NULL) {
34084  /*
34085  If we get here it means we were able to open the stb_vorbis decoder. There may be some leftover bytes in our buffer, so
34086  we need to move those bytes down to the front of the buffer since they'll be needed for future decoding.
34087  */
34088  size_t leftoverDataSize = (dataSize - (size_t)consumedDataSize);
34089  size_t i;
34090  for (i = 0; i < leftoverDataSize; ++i) {
34091  pData[i] = pData[i + consumedDataSize];
34092  }
34093 
34094  dataSize = leftoverDataSize;
34095  break; /* Success. */
34096  } else {
34097  if (vorbisError == VORBIS_need_more_data) {
34098  continue;
34099  } else {
34100  return MA_ERROR; /* Failed to open the stb_vorbis decoder. */
34101  }
34102  }
34103  } while (MA_TRUE);
34104 
34105 
34106  /* If we get here it means we successfully opened the Vorbis decoder. */
34107  vorbisInfo = stb_vorbis_get_info(pInternalVorbis);
34108 
34109  /* Don't allow more than MA_MAX_CHANNELS channels. */
34110  if (vorbisInfo.channels > MA_MAX_CHANNELS) {
34111  stb_vorbis_close(pInternalVorbis);
34112  ma_free(pData);
34113  return MA_ERROR; /* Too many channels. */
34114  }
34115 
34116  vorbisDataSize = sizeof(ma_vorbis_decoder) + sizeof(float)*vorbisInfo.max_frame_size;
34117  pVorbis = (ma_vorbis_decoder*)ma_malloc(vorbisDataSize);
34118  if (pVorbis == NULL) {
34119  stb_vorbis_close(pInternalVorbis);
34120  ma_free(pData);
34121  return MA_OUT_OF_MEMORY;
34122  }
34123 
34124  ma_zero_memory(pVorbis, vorbisDataSize);
34125  pVorbis->pInternalVorbis = pInternalVorbis;
34126  pVorbis->pData = pData;
34127  pVorbis->dataSize = dataSize;
34128  pVorbis->dataCapacity = dataCapacity;
34129 
34130  pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__vorbis;
34131  pDecoder->onUninit = ma_decoder_internal_on_uninit__vorbis;
34132  pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__vorbis;
34133  pDecoder->pInternalDecoder = pVorbis;
34134 
34135  /* The internal format is always f32. */
34136  pDecoder->internalFormat = ma_format_f32;
34137  pDecoder->internalChannels = vorbisInfo.channels;
34138  pDecoder->internalSampleRate = vorbisInfo.sample_rate;
34140 
34141  result = ma_decoder__init_dsp(pDecoder, pConfig, ma_decoder_internal_on_read_pcm_frames__vorbis);
34142  if (result != MA_SUCCESS) {
34143  stb_vorbis_close(pVorbis->pInternalVorbis);
34144  ma_free(pVorbis->pData);
34145  ma_free(pVorbis);
34146  return result;
34147  }
34148 
34149  return MA_SUCCESS;
34150 }
34151 #endif
34152 
34153 /* MP3 */
34154 #ifdef dr_mp3_h
34155 #define MA_HAS_MP3
34156 
34157 size_t ma_decoder_internal_on_read__mp3(void* pUserData, void* pBufferOut, size_t bytesToRead)
34158 {
34159  ma_decoder* pDecoder = (ma_decoder*)pUserData;
34160  ma_assert(pDecoder != NULL);
34161 
34162  return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead);
34163 }
34164 
34165 drmp3_bool32 ma_decoder_internal_on_seek__mp3(void* pUserData, int offset, drmp3_seek_origin origin)
34166 {
34167  ma_decoder* pDecoder = (ma_decoder*)pUserData;
34168  ma_assert(pDecoder != NULL);
34169 
34170  return ma_decoder_seek_bytes(pDecoder, offset, (origin == drmp3_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current);
34171 }
34172 
34173 ma_uint32 ma_decoder_internal_on_read_pcm_frames__mp3(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint32 frameCount, void* pUserData)
34174 {
34175  ma_decoder* pDecoder;
34176  drmp3* pMP3;
34177 
34178  (void)pDSP;
34179 
34180  pDecoder = (ma_decoder*)pUserData;
34181  ma_assert(pDecoder != NULL);
34182  ma_assert(pDecoder->internalFormat == ma_format_f32);
34183 
34184  pMP3 = (drmp3*)pDecoder->pInternalDecoder;
34185  ma_assert(pMP3 != NULL);
34186 
34187  return (ma_uint32)drmp3_read_pcm_frames_f32(pMP3, frameCount, (float*)pFramesOut);
34188 }
34189 
34190 ma_result ma_decoder_internal_on_seek_to_pcm_frame__mp3(ma_decoder* pDecoder, ma_uint64 frameIndex)
34191 {
34192  drmp3* pMP3;
34194 
34195  pMP3 = (drmp3*)pDecoder->pInternalDecoder;
34196  ma_assert(pMP3 != NULL);
34197 
34198  result = drmp3_seek_to_pcm_frame(pMP3, frameIndex);
34199  if (result) {
34200  return MA_SUCCESS;
34201  } else {
34202  return MA_ERROR;
34203  }
34204 }
34205 
34206 ma_result ma_decoder_internal_on_uninit__mp3(ma_decoder* pDecoder)
34207 {
34208  drmp3_uninit((drmp3*)pDecoder->pInternalDecoder);
34209  ma_free(pDecoder->pInternalDecoder);
34210  return MA_SUCCESS;
34211 }
34212 
34213 ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__mp3(ma_decoder* pDecoder)
34214 {
34215  return drmp3_get_pcm_frame_count((drmp3*)pDecoder->pInternalDecoder);
34216 }
34217 
34218 ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34219 {
34220  drmp3* pMP3;
34221  drmp3_config mp3Config;
34222  ma_result result;
34223 
34224  ma_assert(pConfig != NULL);
34225  ma_assert(pDecoder != NULL);
34226 
34227  pMP3 = (drmp3*)ma_malloc(sizeof(*pMP3));
34228  if (pMP3 == NULL) {
34229  return MA_OUT_OF_MEMORY;
34230  }
34231 
34232  /*
34233  Try opening the decoder first. MP3 can have variable sample rates (it's per frame/packet). We therefore need
34234  to use some smarts to determine the most appropriate internal sample rate. These are the rules we're going
34235  to use:
34236 
34237  Sample Rates
34238  1) If an output sample rate is specified in pConfig we just use that. Otherwise;
34239  2) Fall back to 44100.
34240 
34241  The internal channel count is always stereo, and the internal format is always f32.
34242  */
34243  ma_zero_object(&mp3Config);
34244  mp3Config.outputChannels = 2;
34245  mp3Config.outputSampleRate = (pConfig->sampleRate != 0) ? pConfig->sampleRate : 44100;
34246  if (!drmp3_init(pMP3, ma_decoder_internal_on_read__mp3, ma_decoder_internal_on_seek__mp3, pDecoder, &mp3Config, NULL)) {
34247  ma_free(pMP3);
34248  return MA_ERROR;
34249  }
34250 
34251  /* If we get here it means we successfully initialized the MP3 decoder. We can now initialize the rest of the ma_decoder. */
34252  pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__mp3;
34253  pDecoder->onUninit = ma_decoder_internal_on_uninit__mp3;
34254  pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__mp3;
34255  pDecoder->pInternalDecoder = pMP3;
34256 
34257  /* Internal format. */
34258  pDecoder->internalFormat = ma_format_f32;
34259  pDecoder->internalChannels = pMP3->channels;
34260  pDecoder->internalSampleRate = pMP3->sampleRate;
34262 
34263  result = ma_decoder__init_dsp(pDecoder, pConfig, ma_decoder_internal_on_read_pcm_frames__mp3);
34264  if (result != MA_SUCCESS) {
34265  drmp3_uninit(pMP3);
34266  ma_free(pMP3);
34267  return result;
34268  }
34269 
34270  return MA_SUCCESS;
34271 }
34272 #endif
34273 
34274 /* Raw */
34275 ma_uint32 ma_decoder_internal_on_read_pcm_frames__raw(ma_pcm_converter* pDSP, void* pFramesOut, ma_uint32 frameCount, void* pUserData)
34276 {
34277  ma_decoder* pDecoder;
34278  ma_uint32 bpf;
34279 
34280  (void)pDSP;
34281 
34282  pDecoder = (ma_decoder*)pUserData;
34283  ma_assert(pDecoder != NULL);
34284 
34285  /* For raw decoding we just read directly from the decoder's callbacks. */
34286  bpf = ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
34287  return (ma_uint32)ma_decoder_read_bytes(pDecoder, pFramesOut, frameCount * bpf) / bpf;
34288 }
34289 
34290 ma_result ma_decoder_internal_on_seek_to_pcm_frame__raw(ma_decoder* pDecoder, ma_uint64 frameIndex)
34291 {
34293  ma_uint64 totalBytesToSeek;
34294 
34295  ma_assert(pDecoder != NULL);
34296 
34297  if (pDecoder->onSeek == NULL) {
34298  return MA_ERROR;
34299  }
34300 
34301  /* The callback uses a 32 bit integer whereas we use a 64 bit unsigned integer. We just need to continuously seek until we're at the correct position. */
34302  totalBytesToSeek = frameIndex * ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels);
34303  if (totalBytesToSeek < 0x7FFFFFFF) {
34304  /* Simple case. */
34305  result = ma_decoder_seek_bytes(pDecoder, (int)(frameIndex * ma_get_bytes_per_frame(pDecoder->internalFormat, pDecoder->internalChannels)), ma_seek_origin_start);
34306  } else {
34307  /* Complex case. Start by doing a seek relative to the start. Then keep looping using offset seeking. */
34308  result = ma_decoder_seek_bytes(pDecoder, 0x7FFFFFFF, ma_seek_origin_start);
34309  if (result == MA_TRUE) {
34310  totalBytesToSeek -= 0x7FFFFFFF;
34311 
34312  while (totalBytesToSeek > 0) {
34313  ma_uint64 bytesToSeekThisIteration = totalBytesToSeek;
34314  if (bytesToSeekThisIteration > 0x7FFFFFFF) {
34315  bytesToSeekThisIteration = 0x7FFFFFFF;
34316  }
34317 
34318  result = ma_decoder_seek_bytes(pDecoder, (int)bytesToSeekThisIteration, ma_seek_origin_current);
34319  if (result != MA_TRUE) {
34320  break;
34321  }
34322 
34323  totalBytesToSeek -= bytesToSeekThisIteration;
34324  }
34325  }
34326  }
34327 
34328  if (result) {
34329  return MA_SUCCESS;
34330  } else {
34331  return MA_ERROR;
34332  }
34333 }
34334 
34335 ma_result ma_decoder_internal_on_uninit__raw(ma_decoder* pDecoder)
34336 {
34337  (void)pDecoder;
34338  return MA_SUCCESS;
34339 }
34340 
34341 ma_uint64 ma_decoder_internal_on_get_length_in_pcm_frames__raw(ma_decoder* pDecoder)
34342 {
34343  (void)pDecoder;
34344  return 0;
34345 }
34346 
34347 ma_result ma_decoder_init_raw__internal(const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder)
34348 {
34349  ma_result result;
34350 
34351  ma_assert(pConfigIn != NULL);
34352  ma_assert(pConfigOut != NULL);
34353  ma_assert(pDecoder != NULL);
34354 
34355  pDecoder->onSeekToPCMFrame = ma_decoder_internal_on_seek_to_pcm_frame__raw;
34356  pDecoder->onUninit = ma_decoder_internal_on_uninit__raw;
34357  pDecoder->onGetLengthInPCMFrames = ma_decoder_internal_on_get_length_in_pcm_frames__raw;
34358 
34359  /* Internal format. */
34360  pDecoder->internalFormat = pConfigIn->format;
34361  pDecoder->internalChannels = pConfigIn->channels;
34362  pDecoder->internalSampleRate = pConfigIn->sampleRate;
34363  ma_channel_map_copy(pDecoder->internalChannelMap, pConfigIn->channelMap, pConfigIn->channels);
34364 
34365  result = ma_decoder__init_dsp(pDecoder, pConfigOut, ma_decoder_internal_on_read_pcm_frames__raw);
34366  if (result != MA_SUCCESS) {
34367  return result;
34368  }
34369 
34370  return MA_SUCCESS;
34371 }
34372 
34373 ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34374 {
34375  ma_assert(pConfig != NULL);
34376 
34377  if (pDecoder == NULL) {
34378  return MA_INVALID_ARGS;
34379  }
34380 
34381  ma_zero_object(pDecoder);
34382 
34383  if (onRead == NULL || onSeek == NULL) {
34384  return MA_INVALID_ARGS;
34385  }
34386 
34387  pDecoder->onRead = onRead;
34388  pDecoder->onSeek = onSeek;
34389  pDecoder->pUserData = pUserData;
34390 
34391  (void)pConfig;
34392  return MA_SUCCESS;
34393 }
34394 
34395 ma_result ma_decoder_init_wav(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34396 {
34398  ma_result result;
34399 
34400  config = ma_decoder_config_init_copy(pConfig);
34401 
34402  result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
34403  if (result != MA_SUCCESS) {
34404  return result;
34405  }
34406 
34407 #ifdef MA_HAS_WAV
34408  return ma_decoder_init_wav__internal(&config, pDecoder);
34409 #else
34410  return MA_NO_BACKEND;
34411 #endif
34412 }
34413 
34414 ma_result ma_decoder_init_flac(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34415 {
34417  ma_result result;
34418 
34419  config = ma_decoder_config_init_copy(pConfig);
34420 
34421  result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
34422  if (result != MA_SUCCESS) {
34423  return result;
34424  }
34425 
34426 #ifdef MA_HAS_FLAC
34427  return ma_decoder_init_flac__internal(&config, pDecoder);
34428 #else
34429  return MA_NO_BACKEND;
34430 #endif
34431 }
34432 
34433 ma_result ma_decoder_init_vorbis(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34434 {
34436  ma_result result;
34437 
34438  config = ma_decoder_config_init_copy(pConfig);
34439 
34440  result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
34441  if (result != MA_SUCCESS) {
34442  return result;
34443  }
34444 
34445 #ifdef MA_HAS_VORBIS
34446  return ma_decoder_init_vorbis__internal(&config, pDecoder);
34447 #else
34448  return MA_NO_BACKEND;
34449 #endif
34450 }
34451 
34452 ma_result ma_decoder_init_mp3(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34453 {
34455  ma_result result;
34456 
34457  config = ma_decoder_config_init_copy(pConfig);
34458 
34459  result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
34460  if (result != MA_SUCCESS) {
34461  return result;
34462  }
34463 
34464 #ifdef MA_HAS_MP3
34465  return ma_decoder_init_mp3__internal(&config, pDecoder);
34466 #else
34467  return MA_NO_BACKEND;
34468 #endif
34469 }
34470 
34471 ma_result ma_decoder_init_raw(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder)
34472 {
34474  ma_result result;
34475 
34476  config = ma_decoder_config_init_copy(pConfigOut);
34477 
34478  result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
34479  if (result != MA_SUCCESS) {
34480  return result;
34481  }
34482 
34483  return ma_decoder_init_raw__internal(pConfigIn, &config, pDecoder);
34484 }
34485 
34486 ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34487 {
34489 
34490  ma_assert(pConfig != NULL);
34491  ma_assert(pDecoder != NULL);
34492 
34493  /* Silence some warnings in the case that we don't have any decoder backends enabled. */
34494  (void)onRead;
34495  (void)onSeek;
34496  (void)pUserData;
34497  (void)pConfig;
34498  (void)pDecoder;
34499 
34500  /* We use trial and error to open a decoder. */
34501 
34502 #ifdef MA_HAS_WAV
34503  if (result != MA_SUCCESS) {
34504  result = ma_decoder_init_wav__internal(pConfig, pDecoder);
34505  if (result != MA_SUCCESS) {
34506  onSeek(pDecoder, 0, ma_seek_origin_start);
34507  }
34508  }
34509 #endif
34510 #ifdef MA_HAS_FLAC
34511  if (result != MA_SUCCESS) {
34512  result = ma_decoder_init_flac__internal(pConfig, pDecoder);
34513  if (result != MA_SUCCESS) {
34514  onSeek(pDecoder, 0, ma_seek_origin_start);
34515  }
34516  }
34517 #endif
34518 #ifdef MA_HAS_VORBIS
34519  if (result != MA_SUCCESS) {
34520  result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
34521  if (result != MA_SUCCESS) {
34522  onSeek(pDecoder, 0, ma_seek_origin_start);
34523  }
34524  }
34525 #endif
34526 #ifdef MA_HAS_MP3
34527  if (result != MA_SUCCESS) {
34528  result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
34529  if (result != MA_SUCCESS) {
34530  onSeek(pDecoder, 0, ma_seek_origin_start);
34531  }
34532  }
34533 #endif
34534 
34535  if (result != MA_SUCCESS) {
34536  return result;
34537  }
34538 
34539  return result;
34540 }
34541 
34542 ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34543 {
34545  ma_result result;
34546 
34547  config = ma_decoder_config_init_copy(pConfig);
34548 
34549  result = ma_decoder__preinit(onRead, onSeek, pUserData, &config, pDecoder);
34550  if (result != MA_SUCCESS) {
34551  return result;
34552  }
34553 
34554  return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder);
34555 }
34556 
34557 
34558 size_t ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
34559 {
34560  size_t bytesRemaining;
34561 
34562  ma_assert(pDecoder->memory.dataSize >= pDecoder->memory.currentReadPos);
34563 
34564  bytesRemaining = pDecoder->memory.dataSize - pDecoder->memory.currentReadPos;
34565  if (bytesToRead > bytesRemaining) {
34566  bytesToRead = bytesRemaining;
34567  }
34568 
34569  if (bytesToRead > 0) {
34570  ma_copy_memory(pBufferOut, pDecoder->memory.pData + pDecoder->memory.currentReadPos, bytesToRead);
34571  pDecoder->memory.currentReadPos += bytesToRead;
34572  }
34573 
34574  return bytesToRead;
34575 }
34576 
34577 ma_bool32 ma_decoder__on_seek_memory(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin)
34578 {
34579  if (origin == ma_seek_origin_current) {
34580  if (byteOffset > 0) {
34581  if (pDecoder->memory.currentReadPos + byteOffset > pDecoder->memory.dataSize) {
34582  byteOffset = (int)(pDecoder->memory.dataSize - pDecoder->memory.currentReadPos); /* Trying to seek too far forward. */
34583  }
34584  } else {
34585  if (pDecoder->memory.currentReadPos < (size_t)-byteOffset) {
34586  byteOffset = -(int)pDecoder->memory.currentReadPos; /* Trying to seek too far backwards. */
34587  }
34588  }
34589 
34590  /* This will never underflow thanks to the clamps above. */
34591  pDecoder->memory.currentReadPos += byteOffset;
34592  } else {
34593  if ((ma_uint32)byteOffset <= pDecoder->memory.dataSize) {
34594  pDecoder->memory.currentReadPos = byteOffset;
34595  } else {
34596  pDecoder->memory.currentReadPos = pDecoder->memory.dataSize; /* Trying to seek too far forward. */
34597  }
34598  }
34599 
34600  return MA_TRUE;
34601 }
34602 
34603 ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34604 {
34605  ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, pConfig, pDecoder);
34606  if (result != MA_SUCCESS) {
34607  return result;
34608  }
34609 
34610  if (pData == NULL || dataSize == 0) {
34611  return MA_INVALID_ARGS;
34612  }
34613 
34614  pDecoder->memory.pData = (const ma_uint8*)pData;
34615  pDecoder->memory.dataSize = dataSize;
34616  pDecoder->memory.currentReadPos = 0;
34617 
34618  (void)pConfig;
34619  return MA_SUCCESS;
34620 }
34621 
34622 ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34623 {
34625  ma_result result;
34626 
34627  config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
34628 
34629  result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
34630  if (result != MA_SUCCESS) {
34631  return result;
34632  }
34633 
34634  return ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder);
34635 }
34636 
34637 ma_result ma_decoder_init_memory_wav(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34638 {
34640  ma_result result;
34641 
34642  config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
34643 
34644  result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
34645  if (result != MA_SUCCESS) {
34646  return result;
34647  }
34648 
34649 #ifdef MA_HAS_WAV
34650  return ma_decoder_init_wav__internal(&config, pDecoder);
34651 #else
34652  return MA_NO_BACKEND;
34653 #endif
34654 }
34655 
34656 ma_result ma_decoder_init_memory_flac(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34657 {
34659  ma_result result;
34660 
34661  config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
34662 
34663  result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
34664  if (result != MA_SUCCESS) {
34665  return result;
34666  }
34667 
34668 #ifdef MA_HAS_FLAC
34669  return ma_decoder_init_flac__internal(&config, pDecoder);
34670 #else
34671  return MA_NO_BACKEND;
34672 #endif
34673 }
34674 
34675 ma_result ma_decoder_init_memory_vorbis(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34676 {
34678  ma_result result;
34679 
34680  config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
34681 
34682  result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
34683  if (result != MA_SUCCESS) {
34684  return result;
34685  }
34686 
34687 #ifdef MA_HAS_VORBIS
34688  return ma_decoder_init_vorbis__internal(&config, pDecoder);
34689 #else
34690  return MA_NO_BACKEND;
34691 #endif
34692 }
34693 
34694 ma_result ma_decoder_init_memory_mp3(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34695 {
34697  ma_result result;
34698 
34699  config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */
34700 
34701  result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
34702  if (result != MA_SUCCESS) {
34703  return result;
34704  }
34705 
34706 #ifdef MA_HAS_MP3
34707  return ma_decoder_init_mp3__internal(&config, pDecoder);
34708 #else
34709  return MA_NO_BACKEND;
34710 #endif
34711 }
34712 
34713 ma_result ma_decoder_init_memory_raw(const void* pData, size_t dataSize, const ma_decoder_config* pConfigIn, const ma_decoder_config* pConfigOut, ma_decoder* pDecoder)
34714 {
34716  ma_result result;
34717 
34718  config = ma_decoder_config_init_copy(pConfigOut); /* Make sure the config is not NULL. */
34719 
34720  result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder);
34721  if (result != MA_SUCCESS) {
34722  return result;
34723  }
34724 
34725  return ma_decoder_init_raw__internal(pConfigIn, &config, pDecoder);
34726 }
34727 
34728 #ifndef MA_NO_STDIO
34729 #include <stdio.h>
34730 #if !defined(_MSC_VER) && !defined(__DMC__)
34731 #include <strings.h> /* For strcasecmp(). */
34732 #include <wchar.h> /* For wcsrtombs() */
34733 #endif
34734 
34735 const char* ma_path_file_name(const char* path)
34736 {
34737  const char* fileName;
34738 
34739  if (path == NULL) {
34740  return NULL;
34741  }
34742 
34743  fileName = path;
34744 
34745  /* We just loop through the path until we find the last slash. */
34746  while (path[0] != '\0') {
34747  if (path[0] == '/' || path[0] == '\\') {
34748  fileName = path;
34749  }
34750 
34751  path += 1;
34752  }
34753 
34754  /* At this point the file name is sitting on a slash, so just move forward. */
34755  while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
34756  fileName += 1;
34757  }
34758 
34759  return fileName;
34760 }
34761 
34762 const wchar_t* ma_path_file_name_w(const wchar_t* path)
34763 {
34764  const wchar_t* fileName;
34765 
34766  if (path == NULL) {
34767  return NULL;
34768  }
34769 
34770  fileName = path;
34771 
34772  /* We just loop through the path until we find the last slash. */
34773  while (path[0] != '\0') {
34774  if (path[0] == '/' || path[0] == '\\') {
34775  fileName = path;
34776  }
34777 
34778  path += 1;
34779  }
34780 
34781  /* At this point the file name is sitting on a slash, so just move forward. */
34782  while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
34783  fileName += 1;
34784  }
34785 
34786  return fileName;
34787 }
34788 
34789 
34790 const char* ma_path_extension(const char* path)
34791 {
34792  const char* extension;
34793  const char* lastOccurance;
34794 
34795  if (path == NULL) {
34796  path = "";
34797  }
34798 
34799  extension = ma_path_file_name(path);
34800  lastOccurance = NULL;
34801 
34802  /* Just find the last '.' and return. */
34803  while (extension[0] != '\0') {
34804  if (extension[0] == '.') {
34805  extension += 1;
34806  lastOccurance = extension;
34807  }
34808 
34809  extension += 1;
34810  }
34811 
34812  return (lastOccurance != NULL) ? lastOccurance : extension;
34813 }
34814 
34815 const wchar_t* ma_path_extension_w(const wchar_t* path)
34816 {
34817  const wchar_t* extension;
34818  const wchar_t* lastOccurance;
34819 
34820  if (path == NULL) {
34821  path = L"";
34822  }
34823 
34824  extension = ma_path_file_name_w(path);
34825  lastOccurance = NULL;
34826 
34827  /* Just find the last '.' and return. */
34828  while (extension[0] != '\0') {
34829  if (extension[0] == '.') {
34830  extension += 1;
34831  lastOccurance = extension;
34832  }
34833 
34834  extension += 1;
34835  }
34836 
34837  return (lastOccurance != NULL) ? lastOccurance : extension;
34838 }
34839 
34840 
34841 ma_bool32 ma_path_extension_equal(const char* path, const char* extension)
34842 {
34843  const char* ext1;
34844  const char* ext2;
34845 
34846  if (path == NULL || extension == NULL) {
34847  return MA_FALSE;
34848  }
34849 
34850  ext1 = extension;
34851  ext2 = ma_path_extension(path);
34852 
34853 #if defined(_MSC_VER) || defined(__DMC__)
34854  return _stricmp(ext1, ext2) == 0;
34855 #else
34856  return strcasecmp(ext1, ext2) == 0;
34857 #endif
34858 }
34859 
34860 ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension)
34861 {
34862  const wchar_t* ext1;
34863  const wchar_t* ext2;
34864 
34865  if (path == NULL || extension == NULL) {
34866  return MA_FALSE;
34867  }
34868 
34869  ext1 = extension;
34870  ext2 = ma_path_extension_w(path);
34871 
34872 #if defined(_MSC_VER) || defined(__DMC__)
34873  return _wcsicmp(ext1, ext2) == 0;
34874 #else
34875  /*
34876  I'm not aware of a wide character version of strcasecmp(). I'm therefore converting the extensions to multibyte strings and comparing those. This
34877  isn't the most efficient way to do it, but it should work OK.
34878  */
34879  {
34880  char ext1MB[4096];
34881  char ext2MB[4096];
34882  const wchar_t* pext1 = ext1;
34883  const wchar_t* pext2 = ext2;
34884  mbstate_t mbs1;
34885  mbstate_t mbs2;
34886 
34887  ma_zero_object(&mbs1);
34888  ma_zero_object(&mbs2);
34889 
34890  if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) {
34891  return MA_FALSE;
34892  }
34893  if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) {
34894  return MA_FALSE;
34895  }
34896 
34897  return strcasecmp(ext1MB, ext2MB) == 0;
34898  }
34899 #endif
34900 }
34901 
34902 
34903 size_t ma_decoder__on_read_stdio(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead)
34904 {
34905  return fread(pBufferOut, 1, bytesToRead, (FILE*)pDecoder->pUserData);
34906 }
34907 
34908 ma_bool32 ma_decoder__on_seek_stdio(ma_decoder* pDecoder, int byteOffset, ma_seek_origin origin)
34909 {
34910  return fseek((FILE*)pDecoder->pUserData, byteOffset, (origin == ma_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
34911 }
34912 
34913 ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34914 {
34915  FILE* pFile;
34916 
34917  if (pDecoder == NULL) {
34918  return MA_INVALID_ARGS;
34919  }
34920 
34921  ma_zero_object(pDecoder);
34922 
34923  if (pFilePath == NULL || pFilePath[0] == '\0') {
34924  return MA_INVALID_ARGS;
34925  }
34926 
34927 #if defined(_MSC_VER) && _MSC_VER >= 1400
34928  if (fopen_s(&pFile, pFilePath, "rb") != 0) {
34929  return MA_ERROR;
34930  }
34931 #else
34932  pFile = fopen(pFilePath, "rb");
34933  if (pFile == NULL) {
34934  return MA_ERROR;
34935  }
34936 #endif
34937 
34938  /* We need to manually set the user data so the calls to ma_decoder__on_seek_stdio() succeed. */
34939  pDecoder->pUserData = pFile;
34940 
34941  (void)pConfig;
34942  return MA_SUCCESS;
34943 }
34944 
34945 ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
34946 {
34947  FILE* pFile;
34948 
34949  if (pDecoder == NULL) {
34950  return MA_INVALID_ARGS;
34951  }
34952 
34953  ma_zero_object(pDecoder);
34954 
34955  if (pFilePath == NULL || pFilePath[0] == '\0') {
34956  return MA_INVALID_ARGS;
34957  }
34958 
34959 #if defined(_WIN32)
34960  /* Use _wfopen() on Windows. */
34961  #if defined(_MSC_VER) && _MSC_VER >= 1400
34962  if (_wfopen_s(&pFile, pFilePath, L"rb") != 0) {
34963  return MA_ERROR;
34964  }
34965  #else
34966  pFile = _wfopen(pFilePath, L"rb");
34967  if (pFile == NULL) {
34968  return MA_ERROR;
34969  }
34970  #endif
34971 #else
34972  /*
34973  Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
34974  think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for
34975  maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility.
34976  */
34977  {
34978  mbstate_t mbs;
34979  size_t lenMB;
34980  const wchar_t* pFilePathTemp = pFilePath;
34981  char* pFilePathMB = NULL;
34982 
34983  /* Get the length first. */
34984  ma_zero_object(&mbs);
34985  lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
34986  if (lenMB == (size_t)-1) {
34987  return MA_ERROR;
34988  }
34989 
34990  pFilePathMB = (char*)MA_MALLOC(lenMB + 1);
34991  if (pFilePathMB == NULL) {
34992  return MA_OUT_OF_MEMORY;
34993  }
34994 
34995  pFilePathTemp = pFilePath;
34996  ma_zero_object(&mbs);
34997  wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
34998 
34999  pFile = fopen(pFilePathMB, "rb");
35000 
35001  MA_FREE(pFilePathMB);
35002  }
35003 
35004  if (pFile == NULL) {
35005  return MA_ERROR;
35006  }
35007 #endif
35008 
35009  /* We need to manually set the user data so the calls to ma_decoder__on_seek_stdio() succeed. */
35010  pDecoder->pUserData = pFile;
35011 
35012  (void)pConfig;
35013  return MA_SUCCESS;
35014 }
35015 
35016 ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
35017 {
35018  ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder); /* This sets pDecoder->pUserData to a FILE*. */
35019  if (result != MA_SUCCESS) {
35020  return result;
35021  }
35022 
35023  /* WAV */
35024  if (ma_path_extension_equal(pFilePath, "wav")) {
35025  result = ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35026  if (result == MA_SUCCESS) {
35027  return MA_SUCCESS;
35028  }
35029 
35030  ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
35031  }
35032 
35033  /* FLAC */
35034  if (ma_path_extension_equal(pFilePath, "flac")) {
35035  result = ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35036  if (result == MA_SUCCESS) {
35037  return MA_SUCCESS;
35038  }
35039 
35040  ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
35041  }
35042 
35043  /* MP3 */
35044  if (ma_path_extension_equal(pFilePath, "mp3")) {
35045  result = ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35046  if (result == MA_SUCCESS) {
35047  return MA_SUCCESS;
35048  }
35049 
35050  ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
35051  }
35052 
35053  /* Trial and error. */
35054  return ma_decoder_init(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35055 }
35056 
35057 ma_result ma_decoder_init_file_wav(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
35058 {
35059  ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
35060  if (result != MA_SUCCESS) {
35061  return result;
35062  }
35063 
35064  return ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35065 }
35066 
35067 ma_result ma_decoder_init_file_flac(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
35068 {
35069  ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
35070  if (result != MA_SUCCESS) {
35071  return result;
35072  }
35073 
35074  return ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35075 }
35076 
35077 ma_result ma_decoder_init_file_vorbis(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
35078 {
35079  ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
35080  if (result != MA_SUCCESS) {
35081  return result;
35082  }
35083 
35084  return ma_decoder_init_vorbis(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35085 }
35086 
35087 ma_result ma_decoder_init_file_mp3(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
35088 {
35089  ma_result result = ma_decoder__preinit_file(pFilePath, pConfig, pDecoder);
35090  if (result != MA_SUCCESS) {
35091  return result;
35092  }
35093 
35094  return ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35095 }
35096 
35097 
35098 ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
35099 {
35100  ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder); /* This sets pDecoder->pUserData to a FILE*. */
35101  if (result != MA_SUCCESS) {
35102  return result;
35103  }
35104 
35105  /* WAV */
35106  if (ma_path_extension_equal_w(pFilePath, L"wav")) {
35107  result = ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35108  if (result == MA_SUCCESS) {
35109  return MA_SUCCESS;
35110  }
35111 
35112  ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
35113  }
35114 
35115  /* FLAC */
35116  if (ma_path_extension_equal_w(pFilePath, L"flac")) {
35117  result = ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35118  if (result == MA_SUCCESS) {
35119  return MA_SUCCESS;
35120  }
35121 
35122  ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
35123  }
35124 
35125  /* MP3 */
35126  if (ma_path_extension_equal_w(pFilePath, L"mp3")) {
35127  result = ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35128  if (result == MA_SUCCESS) {
35129  return MA_SUCCESS;
35130  }
35131 
35132  ma_decoder__on_seek_stdio(pDecoder, 0, ma_seek_origin_start);
35133  }
35134 
35135  /* Trial and error. */
35136  return ma_decoder_init(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35137 }
35138 
35139 ma_result ma_decoder_init_file_wav_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
35140 {
35141  ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder);
35142  if (result != MA_SUCCESS) {
35143  return result;
35144  }
35145 
35146  return ma_decoder_init_wav(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35147 }
35148 
35149 ma_result ma_decoder_init_file_flac_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
35150 {
35151  ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder);
35152  if (result != MA_SUCCESS) {
35153  return result;
35154  }
35155 
35156  return ma_decoder_init_flac(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35157 }
35158 
35159 ma_result ma_decoder_init_file_vorbis_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
35160 {
35161  ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder);
35162  if (result != MA_SUCCESS) {
35163  return result;
35164  }
35165 
35166  return ma_decoder_init_vorbis(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35167 }
35168 
35169 ma_result ma_decoder_init_file_mp3_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
35170 {
35171  ma_result result = ma_decoder__preinit_file_w(pFilePath, pConfig, pDecoder);
35172  if (result != MA_SUCCESS) {
35173  return result;
35174  }
35175 
35176  return ma_decoder_init_mp3(ma_decoder__on_read_stdio, ma_decoder__on_seek_stdio, pDecoder->pUserData, pConfig, pDecoder);
35177 }
35178 #endif
35179 
35181 {
35182  if (pDecoder == NULL) {
35183  return MA_INVALID_ARGS;
35184  }
35185 
35186  if (pDecoder->onUninit) {
35187  pDecoder->onUninit(pDecoder);
35188  }
35189 
35190 #ifndef MA_NO_STDIO
35191  /* If we have a file handle, close it. */
35192  if (pDecoder->onRead == ma_decoder__on_read_stdio) {
35193  fclose((FILE*)pDecoder->pUserData);
35194  }
35195 #endif
35196 
35197  return MA_SUCCESS;
35198 }
35199 
35201 {
35202  if (pDecoder == NULL) {
35203  return 0;
35204  }
35205 
35206  if (pDecoder->onGetLengthInPCMFrames) {
35207  return pDecoder->onGetLengthInPCMFrames(pDecoder);
35208  }
35209 
35210  return 0;
35211 }
35212 
35213 ma_uint64 ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount)
35214 {
35215  if (pDecoder == NULL) {
35216  return 0;
35217  }
35218 
35219  return ma_pcm_converter_read(&pDecoder->dsp, pFramesOut, frameCount);
35220 }
35221 
35223 {
35224  if (pDecoder == NULL) {
35225  return 0;
35226  }
35227 
35228  if (pDecoder->onSeekToPCMFrame) {
35229  return pDecoder->onSeekToPCMFrame(pDecoder, frameIndex);
35230  }
35231 
35232  /* Should never get here, but if we do it means onSeekToPCMFrame was not set by the backend. */
35233  return MA_INVALID_ARGS;
35234 }
35235 
35236 
35237 ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
35238 {
35239  ma_uint64 totalFrameCount;
35240  ma_uint64 bpf;
35241  ma_uint64 dataCapInFrames;
35242  void* pPCMFramesOut;
35243 
35244  ma_assert(pDecoder != NULL);
35245 
35246  totalFrameCount = 0;
35247  bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
35248 
35249  /* The frame count is unknown until we try reading. Thus, we just run in a loop. */
35250  dataCapInFrames = 0;
35251  pPCMFramesOut = NULL;
35252  for (;;) {
35253  ma_uint64 frameCountToTryReading;
35254  ma_uint64 framesJustRead;
35255 
35256  /* Make room if there's not enough. */
35257  if (totalFrameCount == dataCapInFrames) {
35258  void* pNewPCMFramesOut;
35259  ma_uint64 newDataCapInFrames = dataCapInFrames*2;
35260  if (newDataCapInFrames == 0) {
35261  newDataCapInFrames = 4096;
35262  }
35263 
35264  if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) {
35265  ma_free(pPCMFramesOut);
35266  return MA_TOO_LARGE;
35267  }
35268 
35269 
35270  pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf));
35271  if (pNewPCMFramesOut == NULL) {
35272  ma_free(pPCMFramesOut);
35273  return MA_OUT_OF_MEMORY;
35274  }
35275 
35276  dataCapInFrames = newDataCapInFrames;
35277  pPCMFramesOut = pNewPCMFramesOut;
35278  }
35279 
35280  frameCountToTryReading = dataCapInFrames - totalFrameCount;
35281  ma_assert(frameCountToTryReading > 0);
35282 
35283  framesJustRead = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading);
35284  totalFrameCount += framesJustRead;
35285 
35286  if (framesJustRead < frameCountToTryReading) {
35287  break;
35288  }
35289  }
35290 
35291 
35292  if (pConfigOut != NULL) {
35293  pConfigOut->format = pDecoder->outputFormat;
35294  pConfigOut->channels = pDecoder->outputChannels;
35295  pConfigOut->sampleRate = pDecoder->outputSampleRate;
35296  ma_channel_map_copy(pConfigOut->channelMap, pDecoder->outputChannelMap, pDecoder->outputChannels);
35297  }
35298 
35299  if (ppPCMFramesOut != NULL) {
35300  *ppPCMFramesOut = pPCMFramesOut;
35301  } else {
35302  ma_free(pPCMFramesOut);
35303  }
35304 
35305  if (pFrameCountOut != NULL) {
35306  *pFrameCountOut = totalFrameCount;
35307  }
35308 
35309  ma_decoder_uninit(pDecoder);
35310  return MA_SUCCESS;
35311 }
35312 
35313 #ifndef MA_NO_STDIO
35314 ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
35315 {
35317  ma_decoder decoder;
35318  ma_result result;
35319 
35320  if (pFrameCountOut != NULL) {
35321  *pFrameCountOut = 0;
35322  }
35323  if (ppPCMFramesOut != NULL) {
35324  *ppPCMFramesOut = NULL;
35325  }
35326 
35327  if (pFilePath == NULL) {
35328  return MA_INVALID_ARGS;
35329  }
35330 
35331  config = ma_decoder_config_init_copy(pConfig);
35332 
35333  result = ma_decoder_init_file(pFilePath, &config, &decoder);
35334  if (result != MA_SUCCESS) {
35335  return result;
35336  }
35337 
35338  return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
35339 }
35340 #endif
35341 
35342 ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
35343 {
35345  ma_decoder decoder;
35346  ma_result result;
35347 
35348  if (pFrameCountOut != NULL) {
35349  *pFrameCountOut = 0;
35350  }
35351  if (ppPCMFramesOut != NULL) {
35352  *ppPCMFramesOut = NULL;
35353  }
35354 
35355  if (pData == NULL || dataSize == 0) {
35356  return MA_INVALID_ARGS;
35357  }
35358 
35359  config = ma_decoder_config_init_copy(pConfig);
35360 
35361  result = ma_decoder_init_memory(pData, dataSize, &config, &decoder);
35362  if (result != MA_SUCCESS) {
35363  return result;
35364  }
35365 
35366  return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
35367 }
35368 
35369 #endif /* MA_NO_DECODING */
35370 
35371 
35372 
35373 
35374 /**************************************************************************************************************************************************************
35375 
35376 Generation
35377 
35378 **************************************************************************************************************************************************************/
35379 ma_result ma_sine_wave_init(double amplitude, double periodsPerSecond, ma_uint32 sampleRate, ma_sine_wave* pSineWave)
35380 {
35381  if (pSineWave == NULL) {
35382  return MA_INVALID_ARGS;
35383  }
35384  ma_zero_object(pSineWave);
35385 
35386  if (amplitude == 0 || periodsPerSecond == 0) {
35387  return MA_INVALID_ARGS;
35388  }
35389 
35390  if (amplitude > 1) {
35391  amplitude = 1;
35392  }
35393  if (amplitude < -1) {
35394  amplitude = -1;
35395  }
35396 
35397  pSineWave->amplitude = amplitude;
35398  pSineWave->periodsPerSecond = periodsPerSecond;
35399  pSineWave->delta = MA_TAU_D / sampleRate;
35400  pSineWave->time = 0;
35401 
35402  return MA_SUCCESS;
35403 }
35404 
35405 ma_uint64 ma_sine_wave_read_f32(ma_sine_wave* pSineWave, ma_uint64 count, float* pSamples)
35406 {
35407  return ma_sine_wave_read_f32_ex(pSineWave, count, 1, ma_stream_layout_interleaved, &pSamples);
35408 }
35409 
35410 ma_uint64 ma_sine_wave_read_f32_ex(ma_sine_wave* pSineWave, ma_uint64 frameCount, ma_uint32 channels, ma_stream_layout layout, float** ppFrames)
35411 {
35412  if (pSineWave == NULL) {
35413  return 0;
35414  }
35415 
35416  if (ppFrames != NULL) {
35417  ma_uint64 iFrame;
35418  for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
35419  ma_uint32 iChannel;
35420 
35421  float s = (float)(sin(pSineWave->time * pSineWave->periodsPerSecond) * pSineWave->amplitude);
35422  pSineWave->time += pSineWave->delta;
35423 
35424  if (layout == ma_stream_layout_interleaved) {
35425  for (iChannel = 0; iChannel < channels; iChannel += 1) {
35426  ppFrames[0][iFrame*channels + iChannel] = s;
35427  }
35428  } else {
35429  for (iChannel = 0; iChannel < channels; iChannel += 1) {
35430  ppFrames[iChannel][iFrame] = s;
35431  }
35432  }
35433  }
35434  } else {
35435  pSineWave->time += pSineWave->delta * (ma_int64)frameCount; /* Cast to int64 required for VC6. */
35436  }
35437 
35438  return frameCount;
35439 }
35440 
35441 #if defined(_MSC_VER)
35442  #pragma warning(pop)
35443 #endif
35444 
35445 #endif /* MINIAUDIO_IMPLEMENTATION */
35446 
35447 /*
35448 BACKEND IMPLEMENTATION GUIDELINES
35449 =================================
35450 Context
35451 -------
35452 - Run-time linking if possible.
35453 - Set whether or not it's an asynchronous backend
35454 
35455 Device
35456 ------
35457 - If a full-duplex device is requested and the backend does not support full duplex devices, have ma_device_init__[backend]()
35458  return MA_DEVICE_TYPE_NOT_SUPPORTED.
35459 - If exclusive mode is requested, but the backend does not support it, return MA_SHARE_MODE_NOT_SUPPORTED. If practical, try
35460  not to fall back to a different share mode - give the client exactly what they asked for. Some backends, such as ALSA, may
35461  not have a practical way to distinguish between the two.
35462 - If pDevice->usingDefault* is set, prefer the device's native value if the backend supports it. Otherwise use the relevant
35463  value from the config.
35464 - If the configs buffer size in frames is 0, set it based on the buffer size in milliseconds, keeping in mind to handle the
35465  case when the default sample rate is being used where practical.
35466 - Backends must set the following members of pDevice before returning successfully from ma_device_init__[backend]():
35467  - internalFormat
35468  - internalChannels
35469  - internalSampleRate
35470  - internalChannelMap
35471  - bufferSizeInFrames
35472  - periods
35473 */
35474 
35475 /*
35476 REVISION HISTORY
35477 ================
35478 v0.9.8 - 2019-10-07
35479  - WASAPI: Fix a potential deadlock when starting a full-duplex device.
35480  - WASAPI: Enable automatic resampling by default. Disable with config.wasapi.noAutoConvertSRC.
35481  - Core Audio: Fix bugs with automatic stream routing.
35482  - Add support for controlling whether or not the content of the output buffer passed in to the data callback is pre-initialized
35483  to zero. By default it will be initialized to zero, but this can be changed by setting noPreZeroedOutputBuffer in the device
35484  config. Setting noPreZeroedOutputBuffer to true will leave the contents undefined.
35485  - Add support for clipping samples after the data callback has returned. This only applies when the playback sample format is
35486  configured as ma_format_f32. If you are doing clipping yourself, you can disable this overhead by setting noClip to true in
35487  the device config.
35488  - Add support for master volume control for devices.
35489  - Use ma_device_set_master_volume() to set the volume to a factor between 0 and 1, where 0 is silence and 1 is full volume.
35490  - Use ma_device_set_master_gain_db() to set the volume in decibels where 0 is full volume and < 0 reduces the volume.
35491  - Fix warnings emitted by GCC when `__inline__` is undefined or defined as nothing.
35492 
35493 v0.9.7 - 2019-08-28
35494  - Add support for loopback mode (WASAPI only).
35495  - To use this, set the device type to ma_device_type_loopback, and then fill out the capture section of the device config.
35496  - If you need to capture from a specific output device, set the capture device ID to that of a playback device.
35497  - Fix a crash when an error is posted in ma_device_init().
35498  - Fix a compilation error when compiling for ARM architectures.
35499  - Fix a bug with the audio(4) backend where the device is incorrectly being opened in non-blocking mode.
35500  - Fix memory leaks in the Core Audio backend.
35501  - Minor refactoring to the WinMM, ALSA, PulseAudio, OSS, audio(4), sndio and null backends.
35502 
35503 v0.9.6 - 2019-08-04
35504  - Add support for loading decoders using a wchar_t string for file paths.
35505  - Don't trigger an assert when ma_device_start() is called on a device that is already started. This will now log a warning
35506  and return MA_INVALID_OPERATION. The same applies for ma_device_stop().
35507  - Try fixing an issue with PulseAudio taking a long time to start playback.
35508  - Fix a bug in ma_convert_frames() and ma_convert_frames_ex().
35509  - Fix memory leaks in the WASAPI backend.
35510  - Fix a compilation error with Visual Studio 2010.
35511 
35512 v0.9.5 - 2019-05-21
35513  - Add logging to ma_dlopen() and ma_dlsym().
35514  - Add ma_decoder_get_length_in_pcm_frames().
35515  - Fix a bug with capture on the OpenSL|ES backend.
35516  - Fix a bug with the ALSA backend where a device would not restart after being stopped.
35517 
35518 v0.9.4 - 2019-05-06
35519  - Add support for C89. With this change, miniaudio should compile clean with GCC/Clang with "-std=c89 -ansi -pedantic" and
35520  Microsoft compilers back to VC6. Other compilers should also work, but have not been tested.
35521 
35522 v0.9.3 - 2019-04-19
35523  - Fix compiler errors on GCC when compiling with -std=c99.
35524 
35525 v0.9.2 - 2019-04-08
35526  - Add support for per-context user data.
35527  - Fix a potential bug with context configs.
35528  - Fix some bugs with PulseAudio.
35529 
35530 v0.9.1 - 2019-03-17
35531  - Fix a bug where the output buffer is not getting zeroed out before calling the data callback. This happens when
35532  the device is running in passthrough mode (not doing any data conversion).
35533  - Fix an issue where the data callback is getting called too frequently on the WASAPI and DirectSound backends.
35534  - Fix error on the UWP build.
35535  - Fix a build error on Apple platforms.
35536 
35537 v0.9 - 2019-03-06
35538  - Rebranded to "miniaudio". All namespaces have been renamed from "mal" to "ma".
35539  - API CHANGE: ma_device_init() and ma_device_config_init() have changed significantly:
35540  - The device type, device ID and user data pointer have moved from ma_device_init() to the config.
35541  - All variations of ma_device_config_init_*() have been removed in favor of just ma_device_config_init().
35542  - ma_device_config_init() now takes only one parameter which is the device type. All other properties need
35543  to be set on the returned object directly.
35544  - The onDataCallback and onStopCallback members of ma_device_config have been renamed to "dataCallback"
35545  and "stopCallback".
35546  - The ID of the physical device is now split into two: one for the playback device and the other for the
35547  capture device. This is required for full-duplex. These are named "pPlaybackDeviceID" and "pCaptureDeviceID".
35548  - API CHANGE: The data callback has changed. It now uses a unified callback for all device types rather than
35549  being separate for each. It now takes two pointers - one containing input data and the other output data. This
35550  design in required for full-duplex. The return value is now void instead of the number of frames written. The
35551  new callback looks like the following:
35552  void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
35553  - API CHANGE: Remove the log callback parameter from ma_context_config_init(). With this change,
35554  ma_context_config_init() now takes no parameters and the log callback is set via the structure directly. The
35555  new policy for config initialization is that only mandatory settings are passed in to *_config_init(). The
35556  "onLog" member of ma_context_config has been renamed to "logCallback".
35557  - API CHANGE: Remove ma_device_get_buffer_size_in_bytes().
35558  - API CHANGE: Rename decoding APIs to "pcm_frames" convention.
35559  - mal_decoder_read() -> ma_decoder_read_pcm_frames()
35560  - mal_decoder_seek_to_frame() -> ma_decoder_seek_to_pcm_frame()
35561  - API CHANGE: Rename sine wave reading APIs to f32 convention.
35562  - mal_sine_wave_read() -> ma_sine_wave_read_f32()
35563  - mal_sine_wave_read_ex() -> ma_sine_wave_read_f32_ex()
35564  - API CHANGE: Remove some deprecated APIs
35565  - mal_device_set_recv_callback()
35566  - mal_device_set_send_callback()
35567  - mal_src_set_input_sample_rate()
35568  - mal_src_set_output_sample_rate()
35569  - API CHANGE: Add log level to the log callback. New signature:
35570  - void on_log(ma_context* pContext, ma_device* pDevice, ma_uint32 logLevel, const char* message)
35571  - API CHANGE: Changes to result codes. Constants have changed and unused codes have been removed. If you're
35572  a binding mainainer you will need to update your result code constants.
35573  - API CHANGE: Change the order of the ma_backend enums to priority order. If you are a binding maintainer, you
35574  will need to update.
35575  - API CHANGE: Rename mal_dsp to ma_pcm_converter. All functions have been renamed from mal_dsp_*() to
35576  ma_pcm_converter_*(). All structures have been renamed from mal_dsp* to ma_pcm_converter*.
35577  - API CHANGE: Reorder parameters of ma_decoder_read_pcm_frames() to be consistent with the new parameter order scheme.
35578  - The resampling algorithm has been changed from sinc to linear. The rationale for this is that the sinc implementation
35579  is too inefficient right now. This will hopefully be improved at a later date.
35580  - Device initialization will no longer fall back to shared mode if exclusive mode is requested but is unusable.
35581  With this change, if you request an device in exclusive mode, but exclusive mode is not supported, it will not
35582  automatically fall back to shared mode. The client will need to reinitialize the device in shared mode if that's
35583  what they want.
35584  - Add ring buffer API. This is ma_rb and ma_pcm_rb, the difference being that ma_rb operates on bytes and
35585  ma_pcm_rb operates on PCM frames.
35586  - Add Web Audio backend. This is used when compiling with Emscripten. The SDL backend, which was previously
35587  used for web support, will be removed in a future version.
35588  - Add AAudio backend (Android Audio). This is the new priority backend for Android. Support for AAudio starts
35589  with Android 8. OpenSL|ES is used as a fallback for older versions of Android.
35590  - Remove OpenAL and SDL backends.
35591  - Fix a possible deadlock when rapidly stopping the device after it has started.
35592  - Update documentation.
35593  - Change licensing to a choice of public domain _or_ MIT-0 (No Attribution).
35594 
35595 v0.8.14 - 2018-12-16
35596  - Core Audio: Fix a bug where the device state is not set correctly after stopping.
35597  - Add support for custom weights to the channel router.
35598  - Update decoders to use updated APIs in dr_flac, dr_mp3 and dr_wav.
35599 
35600 v0.8.13 - 2018-12-04
35601  - Core Audio: Fix a bug with channel mapping.
35602  - Fix a bug with channel routing where the back/left and back/right channels have the wrong weight.
35603 
35604 v0.8.12 - 2018-11-27
35605  - Drop support for SDL 1.2. The Emscripten build now requires "-s USE_SDL=2".
35606  - Fix a linking error with ALSA.
35607  - Fix a bug on iOS where the device name is not set correctly.
35608 
35609 v0.8.11 - 2018-11-21
35610  - iOS bug fixes.
35611  - Minor tweaks to PulseAudio.
35612 
35613 v0.8.10 - 2018-10-21
35614  - Core Audio: Fix a hang when uninitializing a device.
35615  - Fix a bug where an incorrect value is returned from mal_device_stop().
35616 
35617 v0.8.9 - 2018-09-28
35618  - Fix a bug with the SDL backend where device initialization fails.
35619 
35620 v0.8.8 - 2018-09-14
35621  - Fix Linux build with the ALSA backend.
35622  - Minor documentation fix.
35623 
35624 v0.8.7 - 2018-09-12
35625  - Fix a bug with UWP detection.
35626 
35627 v0.8.6 - 2018-08-26
35628  - Automatically switch the internal device when the default device is unplugged. Note that this is still in the
35629  early stages and not all backends handle this the same way. As of this version, this will not detect a default
35630  device switch when changed from the operating system's audio preferences (unless the backend itself handles
35631  this automatically). This is not supported in exclusive mode.
35632  - WASAPI and Core Audio: Add support for stream routing. When the application is using a default device and the
35633  user switches the default device via the operating system's audio preferences, miniaudio will automatically switch
35634  the internal device to the new default. This is not supported in exclusive mode.
35635  - WASAPI: Add support for hardware offloading via IAudioClient2. Only supported on Windows 8 and newer.
35636  - WASAPI: Add support for low-latency shared mode via IAudioClient3. Only supported on Windows 10 and newer.
35637  - Add support for compiling the UWP build as C.
35638  - mal_device_set_recv_callback() and mal_device_set_send_callback() have been deprecated. You must now set this
35639  when the device is initialized with mal_device_init*(). These will be removed in version 0.9.0.
35640 
35641 v0.8.5 - 2018-08-12
35642  - Add support for specifying the size of a device's buffer in milliseconds. You can still set the buffer size in
35643  frames if that suits you. When bufferSizeInFrames is 0, bufferSizeInMilliseconds will be used. If both are non-0
35644  then bufferSizeInFrames will take priority. If both are set to 0 the default buffer size is used.
35645  - Add support for the audio(4) backend to OpenBSD.
35646  - Fix a bug with the ALSA backend that was causing problems on Raspberry Pi. This significantly improves the
35647  Raspberry Pi experience.
35648  - Fix a bug where an incorrect number of samples is returned from sinc resampling.
35649  - Add support for setting the value to be passed to internal calls to CoInitializeEx().
35650  - WASAPI and WinMM: Stop the device when it is unplugged.
35651 
35652 v0.8.4 - 2018-08-06
35653  - Add sndio backend for OpenBSD.
35654  - Add audio(4) backend for NetBSD.
35655  - Drop support for the OSS backend on everything except FreeBSD and DragonFly BSD.
35656  - Formats are now native-endian (were previously little-endian).
35657  - Mark some APIs as deprecated:
35658  - mal_src_set_input_sample_rate() and mal_src_set_output_sample_rate() are replaced with mal_src_set_sample_rate().
35659  - mal_dsp_set_input_sample_rate() and mal_dsp_set_output_sample_rate() are replaced with mal_dsp_set_sample_rate().
35660  - Fix a bug when capturing using the WASAPI backend.
35661  - Fix some aliasing issues with resampling, specifically when increasing the sample rate.
35662  - Fix warnings.
35663 
35664 v0.8.3 - 2018-07-15
35665  - Fix a crackling bug when resampling in capture mode.
35666  - Core Audio: Fix a bug where capture does not work.
35667  - ALSA: Fix a bug where the worker thread can get stuck in an infinite loop.
35668  - PulseAudio: Fix a bug where mal_context_init() succeeds when PulseAudio is unusable.
35669  - JACK: Fix a bug where mal_context_init() succeeds when JACK is unusable.
35670 
35671 v0.8.2 - 2018-07-07
35672  - Fix a bug on macOS with Core Audio where the internal callback is not called.
35673 
35674 v0.8.1 - 2018-07-06
35675  - Fix compilation errors and warnings.
35676 
35677 v0.8 - 2018-07-05
35678  - Changed MAL_IMPLEMENTATION to MINI_AL_IMPLEMENTATION for consistency with other libraries. The old
35679  way is still supported for now, but you should update as it may be removed in the future.
35680  - API CHANGE: Replace device enumeration APIs. mal_enumerate_devices() has been replaced with
35681  mal_context_get_devices(). An additional low-level device enumration API has been introduced called
35682  mal_context_enumerate_devices() which uses a callback to report devices.
35683  - API CHANGE: Rename mal_get_sample_size_in_bytes() to mal_get_bytes_per_sample() and add
35684  mal_get_bytes_per_frame().
35685  - API CHANGE: Replace mal_device_config.preferExclusiveMode with mal_device_config.shareMode.
35686  - This new config can be set to mal_share_mode_shared (default) or mal_share_mode_exclusive.
35687  - API CHANGE: Remove excludeNullDevice from mal_context_config.alsa.
35688  - API CHANGE: Rename MAL_MAX_SAMPLE_SIZE_IN_BYTES to MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES.
35689  - API CHANGE: Change the default channel mapping to the standard Microsoft mapping.
35690  - API CHANGE: Remove backend-specific result codes.
35691  - API CHANGE: Changes to the format conversion APIs (mal_pcm_f32_to_s16(), etc.)
35692  - Add support for Core Audio (Apple).
35693  - Add support for PulseAudio.
35694  - This is the highest priority backend on Linux (higher priority than ALSA) since it is commonly
35695  installed by default on many of the popular distros and offer's more seamless integration on
35696  platforms where PulseAudio is used. In addition, if PulseAudio is installed and running (which
35697  is extremely common), it's better to just use PulseAudio directly rather than going through the
35698  "pulse" ALSA plugin (which is what the "default" ALSA device is likely set to).
35699  - Add support for JACK.
35700  - Remove dependency on asound.h for the ALSA backend. This means the ALSA development packages are no
35701  longer required to build miniaudio.
35702  - Remove dependency on dsound.h for the DirectSound backend. This fixes build issues with some
35703  distributions of MinGW.
35704  - Remove dependency on audioclient.h for the WASAPI backend. This fixes build issues with some
35705  distributions of MinGW.
35706  - Add support for dithering to format conversion.
35707  - Add support for configuring the priority of the worker thread.
35708  - Add a sine wave generator.
35709  - Improve efficiency of sample rate conversion.
35710  - Introduce the notion of standard channel maps. Use mal_get_standard_channel_map().
35711  - Introduce the notion of default device configurations. A default config uses the same configuration
35712  as the backend's internal device, and as such results in a pass-through data transmission pipeline.
35713  - Add support for passing in NULL for the device config in mal_device_init(), which uses a default
35714  config. This requires manually calling mal_device_set_send/recv_callback().
35715  - Add support for decoding from raw PCM data (mal_decoder_init_raw(), etc.)
35716  - Make mal_device_init_ex() more robust.
35717  - Make some APIs more const-correct.
35718  - Fix errors with SDL detection on Apple platforms.
35719  - Fix errors with OpenAL detection.
35720  - Fix some memory leaks.
35721  - Fix a bug with opening decoders from memory.
35722  - Early work on SSE2, AVX2 and NEON optimizations.
35723  - Miscellaneous bug fixes.
35724  - Documentation updates.
35725 
35726 v0.7 - 2018-02-25
35727  - API CHANGE: Change mal_src_read_frames() and mal_dsp_read_frames() to use 64-bit sample counts.
35728  - Add decoder APIs for loading WAV, FLAC, Vorbis and MP3 files.
35729  - Allow opening of devices without a context.
35730  - In this case the context is created and managed internally by the device.
35731  - Change the default channel mapping to the same as that used by FLAC.
35732  - Fix build errors with macOS.
35733 
35734 v0.6c - 2018-02-12
35735  - Fix build errors with BSD/OSS.
35736 
35737 v0.6b - 2018-02-03
35738  - Fix some warnings when compiling with Visual C++.
35739 
35740 v0.6a - 2018-01-26
35741  - Fix errors with channel mixing when increasing the channel count.
35742  - Improvements to the build system for the OpenAL backend.
35743  - Documentation fixes.
35744 
35745 v0.6 - 2017-12-08
35746  - API CHANGE: Expose and improve mutex APIs. If you were using the mutex APIs before this version you'll
35747  need to update.
35748  - API CHANGE: SRC and DSP callbacks now take a pointer to a mal_src and mal_dsp object respectively.
35749  - API CHANGE: Improvements to event and thread APIs. These changes make these APIs more consistent.
35750  - Add support for SDL and Emscripten.
35751  - Simplify the build system further for when development packages for various backends are not installed.
35752  With this change, when the compiler supports __has_include, backends without the relevant development
35753  packages installed will be ignored. This fixes the build for old versions of MinGW.
35754  - Fixes to the Android build.
35755  - Add mal_convert_frames(). This is a high-level helper API for performing a one-time, bulk conversion of
35756  audio data to a different format.
35757  - Improvements to f32 -> u8/s16/s24/s32 conversion routines.
35758  - Fix a bug where the wrong value is returned from mal_device_start() for the OpenSL backend.
35759  - Fixes and improvements for Raspberry Pi.
35760  - Warning fixes.
35761 
35762 v0.5 - 2017-11-11
35763  - API CHANGE: The mal_context_init() function now takes a pointer to a mal_context_config object for
35764  configuring the context. The works in the same kind of way as the device config. The rationale for this
35765  change is to give applications better control over context-level properties, add support for backend-
35766  specific configurations, and support extensibility without breaking the API.
35767  - API CHANGE: The alsa.preferPlugHW device config variable has been removed since it's not really useful for
35768  anything anymore.
35769  - ALSA: By default, device enumeration will now only enumerate over unique card/device pairs. Applications
35770  can enable verbose device enumeration by setting the alsa.useVerboseDeviceEnumeration context config
35771  variable.
35772  - ALSA: When opening a device in shared mode (the default), the dmix/dsnoop plugin will be prioritized. If
35773  this fails it will fall back to the hw plugin. With this change the preferExclusiveMode config is now
35774  honored. Note that this does not happen when alsa.useVerboseDeviceEnumeration is set to true (see above)
35775  which is by design.
35776  - ALSA: Add support for excluding the "null" device using the alsa.excludeNullDevice context config variable.
35777  - ALSA: Fix a bug with channel mapping which causes an assertion to fail.
35778  - Fix errors with enumeration when pInfo is set to NULL.
35779  - OSS: Fix a bug when starting a device when the client sends 0 samples for the initial buffer fill.
35780 
35781 v0.4 - 2017-11-05
35782  - API CHANGE: The log callback is now per-context rather than per-device and as is thus now passed to
35783  mal_context_init(). The rationale for this change is that it allows applications to capture diagnostic
35784  messages at the context level. Previously this was only available at the device level.
35785  - API CHANGE: The device config passed to mal_device_init() is now const.
35786  - Added support for OSS which enables support on BSD platforms.
35787  - Added support for WinMM (waveOut/waveIn).
35788  - Added support for UWP (Universal Windows Platform) applications. Currently C++ only.
35789  - Added support for exclusive mode for selected backends. Currently supported on WASAPI.
35790  - POSIX builds no longer require explicit linking to libpthread (-lpthread).
35791  - ALSA: Explicit linking to libasound (-lasound) is no longer required.
35792  - ALSA: Latency improvements.
35793  - ALSA: Use MMAP mode where available. This can be disabled with the alsa.noMMap config.
35794  - ALSA: Use "hw" devices instead of "plughw" devices by default. This can be disabled with the
35795  alsa.preferPlugHW config.
35796  - WASAPI is now the highest priority backend on Windows platforms.
35797  - Fixed an error with sample rate conversion which was causing crackling when capturing.
35798  - Improved error handling.
35799  - Improved compiler support.
35800  - Miscellaneous bug fixes.
35801 
35802 v0.3 - 2017-06-19
35803  - API CHANGE: Introduced the notion of a context. The context is the highest level object and is required for
35804  enumerating and creating devices. Now, applications must first create a context, and then use that to
35805  enumerate and create devices. The reason for this change is to ensure device enumeration and creation is
35806  tied to the same backend. In addition, some backends are better suited to this design.
35807  - API CHANGE: Removed the rewinding APIs because they're too inconsistent across the different backends, hard
35808  to test and maintain, and just generally unreliable.
35809  - Added helper APIs for initializing mal_device_config objects.
35810  - Null Backend: Fixed a crash when recording.
35811  - Fixed build for UWP.
35812  - Added support for f32 formats to the OpenSL|ES backend.
35813  - Added initial implementation of the WASAPI backend.
35814  - Added initial implementation of the OpenAL backend.
35815  - Added support for low quality linear sample rate conversion.
35816  - Added early support for basic channel mapping.
35817 
35818 v0.2 - 2016-10-28
35819  - API CHANGE: Add user data pointer as the last parameter to mal_device_init(). The rationale for this
35820  change is to ensure the logging callback has access to the user data during initialization.
35821  - API CHANGE: Have device configuration properties be passed to mal_device_init() via a structure. Rationale:
35822  1) The number of parameters is just getting too much.
35823  2) It makes it a bit easier to add new configuration properties in the future. In particular, there's a
35824  chance there will be support added for backend-specific properties.
35825  - Dropped support for f64, A-law and Mu-law formats since they just aren't common enough to justify the
35826  added maintenance cost.
35827  - DirectSound: Increased the default buffer size for capture devices.
35828  - Added initial implementation of the OpenSL|ES backend.
35829 
35830 v0.1 - 2016-10-21
35831  - Initial versioned release.
35832 */
35833 
35834 
35835 /*
35836 This software is available as a choice of the following licenses. Choose
35837 whichever you prefer.
35838 
35839 ===============================================================================
35840 ALTERNATIVE 1 - Public Domain (www.unlicense.org)
35841 ===============================================================================
35842 This is free and unencumbered software released into the public domain.
35843 
35844 Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
35845 software, either in source code form or as a compiled binary, for any purpose,
35846 commercial or non-commercial, and by any means.
35847 
35848 In jurisdictions that recognize copyright laws, the author or authors of this
35849 software dedicate any and all copyright interest in the software to the public
35850 domain. We make this dedication for the benefit of the public at large and to
35851 the detriment of our heirs and successors. We intend this dedication to be an
35852 overt act of relinquishment in perpetuity of all present and future rights to
35853 this software under copyright law.
35854 
35855 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35856 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35857 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35858 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
35859 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35860 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35861 
35862 For more information, please refer to <http://unlicense.org/>
35863 
35864 ===============================================================================
35865 ALTERNATIVE 2 - MIT No Attribution
35866 ===============================================================================
35867 Copyright 2019 David Reid
35868 
35869 Permission is hereby granted, free of charge, to any person obtaining a copy of
35870 this software and associated documentation files (the "Software"), to deal in
35871 the Software without restriction, including without limitation the rights to
35872 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
35873 of the Software, and to permit persons to whom the Software is furnished to do
35874 so.
35875 
35876 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35877 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35878 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35879 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
35880 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
35881 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35882 SOFTWARE.
35883 */
ma_log_proc
void(* ma_log_proc)(ma_context *pContext, ma_device *pDevice, ma_uint32 logLevel, const char *message)
Definition: miniaudio.h:1843
ma_pcm_converter_config::srcAlgorithm
ma_src_algorithm srcAlgorithm
Definition: miniaudio.h:994
MA_LOG_LEVEL
#define MA_LOG_LEVEL
Definition: miniaudio.h:642
ma_device_config::bufferSizeInMilliseconds
ma_uint32 bufferSizeInMilliseconds
Definition: miniaudio.h:1938
ma_src_sinc_window_function
ma_src_sinc_window_function
Definition: miniaudio.h:922
ma_format_converter_config::noNEON
ma_bool32 noNEON
Definition: miniaudio.h:855
ma_pcm_rb_available_read
ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb *pRB)
MA_CHANNEL_AUX_30
#define MA_CHANNEL_AUX_30
Definition: miniaudio.h:699
ma_device_config::pDeviceID
ma_device_id * pDeviceID
Definition: miniaudio.h:1948
MA_CHANNEL_AUX_24
#define MA_CHANNEL_AUX_24
Definition: miniaudio.h:693
ma_pcm_rb_get_subbuffer_size
ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb *pRB)
ma_seek_origin_current
@ ma_seek_origin_current
Definition: miniaudio.h:3168
MA_SAMPLE_RATE_88200
#define MA_SAMPLE_RATE_88200
Definition: miniaudio.h:759
ma_pcm_s16_to_f32
void ma_pcm_s16_to_f32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_device_config::shareMode
ma_share_mode shareMode
Definition: miniaudio.h:1952
ma_thread_priority_default
@ ma_thread_priority_default
Definition: miniaudio.h:1730
ma_channel_router_config::channelMapIn
ma_channel channelMapIn[MA_MAX_CHANNELS]
Definition: miniaudio.h:882
drwav::bitsPerSample
drwav_uint16 bitsPerSample
Definition: dr_wav.h:413
drmp3::channels
drmp3_uint32 channels
Definition: dr_mp3.h:242
ma_context::playbackDeviceInfoCount
ma_uint32 playbackDeviceInfoCount
Definition: miniaudio.h:2013
ma_src_algorithm_linear
@ ma_src_algorithm_linear
Definition: miniaudio.h:915
MA_FALSE
#define MA_FALSE
Definition: miniaudio.h:573
ma_pcm_rb_acquire_write
ma_result ma_pcm_rb_acquire_write(ma_pcm_rb *pRB, ma_uint32 *pSizeInFrames, void **ppBufferOut)
size
GLsizeiptr size
Definition: glad.h:1291
drflac_seek_origin
drflac_seek_origin
Definition: dr_flac.h:226
drwav_seek_origin
drwav_seek_origin
Definition: dr_wav.h:205
uint8_t
unsigned char uint8_t
Definition: stdint.h:78
ma_standard_channel_map_alsa
@ ma_standard_channel_map_alsa
Definition: miniaudio.h:823
ma_pcm_rb_commit_write
ma_result ma_pcm_rb_commit_write(ma_pcm_rb *pRB, ma_uint32 sizeInFrames, void *pBufferOut)
ma_src_config::sampleRateOut
ma_uint32 sampleRateOut
Definition: miniaudio.h:937
MA_FAILED_TO_STOP_BACKEND_DEVICE
#define MA_FAILED_TO_STOP_BACKEND_DEVICE
Definition: miniaudio.h:743
ma_rb::encodedReadOffset
volatile ma_uint32 encodedReadOffset
Definition: miniaudio.h:1497
ma_decoder_init_flac
ma_result ma_decoder_init_flac(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void *pUserData, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_pcm_rb_seek_write
ma_result ma_pcm_rb_seek_write(ma_pcm_rb *pRB, ma_uint32 offsetInFrames)
ma_thread_priority
ma_thread_priority
Definition: miniaudio.h:1722
int8_t
signed char int8_t
Definition: stdint.h:75
DR_WAVE_FORMAT_IEEE_FLOAT
#define DR_WAVE_FORMAT_IEEE_FLOAT
Definition: dr_wav.h:190
ma_thread
Definition: miniaudio.h:1734
MA_CHANNEL_AUX_1
#define MA_CHANNEL_AUX_1
Definition: miniaudio.h:670
drflac_open
drflac * drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void *pUserData)
MA_SAMPLE_RATE_8000
#define MA_SAMPLE_RATE_8000
Definition: miniaudio.h:751
ma_rb::subbufferStrideInBytes
ma_uint32 subbufferStrideInBytes
Definition: miniaudio.h:1496
ma_pcm_convert
void ma_pcm_convert(void *pOut, ma_format formatOut, const void *pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
AudioBuffer
#define AudioBuffer
Definition: raudio.c:221
ma_decoder::dsp
ma_pcm_converter dsp
Definition: miniaudio.h:3206
ma_rb::subbufferCount
ma_uint32 subbufferCount
Definition: miniaudio.h:1495
ma_decoder_config::sinc
ma_src_config_sinc sinc
Definition: miniaudio.h:3188
ma_dither_mode
ma_dither_mode
Definition: miniaudio.h:790
ma_device_config::noAutoConvertSRC
ma_bool32 noAutoConvertSRC
Definition: miniaudio.h:1965
MA_FAILED_TO_UNMAP_DEVICE_BUFFER
#define MA_FAILED_TO_UNMAP_DEVICE_BUFFER
Definition: miniaudio.h:735
ma_standard_channel_map_vorbis
@ ma_standard_channel_map_vorbis
Definition: miniaudio.h:826
ma_decoder_seek_to_pcm_frame_proc
ma_result(* ma_decoder_seek_to_pcm_frame_proc)(ma_decoder *pDecoder, ma_uint64 frameIndex)
Definition: miniaudio.h:3173
MA_CHANNEL_AUX_28
#define MA_CHANNEL_AUX_28
Definition: miniaudio.h:697
ma_backend_dsound
@ ma_backend_dsound
Definition: miniaudio.h:1705
ma_pcm_converter_config::channelMapOut
ma_channel channelMapOut[MA_MAX_CHANNELS]
Definition: miniaudio.h:991
ma_decoder_init_memory_mp3
ma_result ma_decoder_init_memory_mp3(const void *pData, size_t dataSize, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_timer::counter
ma_int64 counter
Definition: miniaudio.h:1929
label
GLuint GLsizei const GLchar * label
Definition: gl2ext.h:1204
ma_device_type_loopback
@ ma_device_type_loopback
Definition: miniaudio.h:1850
ma_context::pUserData
void * pUserData
Definition: miniaudio.h:2009
ma_channel_router_config::weights
float weights[MA_MAX_CHANNELS][MA_MAX_CHANNELS]
Definition: miniaudio.h:885
stb_vorbis_open_pushdata
STBVDEF stb_vorbis * stb_vorbis_open_pushdata(const unsigned char *datablock, int datablock_length_in_bytes, int *datablock_memory_consumed_in_bytes, int *error, const stb_vorbis_alloc *alloc_buffer)
ma_rb::clearOnWriteAcquire
ma_bool32 clearOnWriteAcquire
Definition: miniaudio.h:1500
ma_calculate_buffer_size_in_frames_from_milliseconds
ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate)
ma_format_converter_read_deinterleaved
ma_uint64 ma_format_converter_read_deinterleaved(ma_format_converter *pConverter, ma_uint64 frameCount, void **ppSamplesOut, void *pUserData)
drmp3_init
drmp3_bool32 drmp3_init(drmp3 *pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void *pUserData, const drmp3_config *pConfig)
ma_decoder_config::sampleRate
ma_uint32 sampleRate
Definition: miniaudio.h:3181
ma_context::pthread_attr_setschedparam
ma_proc pthread_attr_setschedparam
Definition: miniaudio.h:2369
flags
GLbitfield flags
Definition: glad.h:1932
MA_CHANNEL_AUX_6
#define MA_CHANNEL_AUX_6
Definition: miniaudio.h:675
MA_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES
#define MA_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES
Definition: miniaudio.h:776
ma_context_config::pulse
struct ma_context_config::@73 pulse
ma_rb_commit_write
ma_result ma_rb_commit_write(ma_rb *pRB, size_t sizeInBytes, void *pBufferOut)
ma_channel_map_valid
ma_bool32 ma_channel_map_valid(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS])
DR_WAVE_FORMAT_PCM
#define DR_WAVE_FORMAT_PCM
Definition: dr_wav.h:188
ma_format_converter_config::streamFormatIn
ma_stream_format streamFormatIn
Definition: miniaudio.h:849
ma_copy_and_apply_volume_factor_pcm_frames
void ma_copy_and_apply_volume_factor_pcm_frames(void *pFramesOut, const void *pFramesIn, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor)
ma_uintptr
uintptr_t ma_uintptr
Definition: miniaudio.h:551
ma_src_algorithm_none
@ ma_src_algorithm_none
Definition: miniaudio.h:917
ma_format_converter_config_init_new
ma_format_converter_config ma_format_converter_config_init_new(void)
first
GLint first
Definition: glad.h:1134
drwav_int32
int32_t drwav_int32
Definition: dr_wav.h:173
MA_ACCESS_DENIED
#define MA_ACCESS_DENIED
Definition: miniaudio.h:714
MA_SAMPLE_RATE_24000
#define MA_SAMPLE_RATE_24000
Definition: miniaudio.h:755
MA_CHANNEL_BACK_CENTER
#define MA_CHANNEL_BACK_CENTER
Definition: miniaudio.h:659
ma_pcm_s32_to_s16
void ma_pcm_s32_to_s16(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
drflac
Definition: dr_flac.h:488
val
GLuint GLfloat * val
Definition: glad.h:1968
value
GLsizei const GLfloat * value
Definition: glad.h:1448
ma_context::deviceInfoCapacity
ma_uint32 deviceInfoCapacity
Definition: miniaudio.h:2012
ma_pcm_converter_config::sampleRateOut
ma_uint32 sampleRateOut
Definition: miniaudio.h:990
drflac::channels
drflac_uint8 channels
Definition: dr_flac.h:503
ma_pcm_s16_to_s32
void ma_pcm_s16_to_s32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
uint16_t
unsigned short uint16_t
Definition: stdint.h:79
ma_decoder_config
Definition: miniaudio.h:3178
ma_pcm_rb::rb
ma_rb rb
Definition: miniaudio.h:1471
ma_copy_and_apply_volume_factor_s16
void ma_copy_and_apply_volume_factor_s16(ma_int16 *pSamplesOut, const ma_int16 *pSamplesIn, ma_uint32 sampleCount, float factor)
ma_device_config::deviceType
ma_device_type deviceType
Definition: miniaudio.h:1935
ma_apply_volume_factor_u8
void ma_apply_volume_factor_u8(ma_uint8 *pSamples, ma_uint32 sampleCount, float factor)
MA_SAMPLE_RATE_44100
#define MA_SAMPLE_RATE_44100
Definition: miniaudio.h:757
ma_thread_priority_high
@ ma_thread_priority_high
Definition: miniaudio.h:1727
MA_CHANNEL_TOP_FRONT_LEFT
#define MA_CHANNEL_TOP_FRONT_LEFT
Definition: miniaudio.h:663
drmp3_config
Definition: dr_mp3.h:233
ma_channel_router::isStereoToMono
ma_bool32 isStereoToMono
Definition: miniaudio.h:900
ma_device_callback_proc
void(* ma_device_callback_proc)(ma_device *pDevice, void *pOutput, const void *pInput, ma_uint32 frameCount)
Definition: miniaudio.h:1819
ma_src_config::neverConsumeEndOfInput
ma_bool32 neverConsumeEndOfInput
Definition: miniaudio.h:940
drflac_seek_to_pcm_frame
drflac_bool32 drflac_seek_to_pcm_frame(drflac *pFlac, drflac_uint64 pcmFrameIndex)
NULL
#define NULL
Definition: miniaudio.h:585
MA_SAMPLE_RATE_22050
#define MA_SAMPLE_RATE_22050
Definition: miniaudio.h:754
MA_CHANNEL_AUX_5
#define MA_CHANNEL_AUX_5
Definition: miniaudio.h:674
f
GLfloat f
Definition: glad.h:2893
ma_backend_opensl
@ ma_backend_opensl
Definition: miniaudio.h:1715
ma_decoder_get_length_in_pcm_frames
ma_uint64 ma_decoder_get_length_in_pcm_frames(ma_decoder *pDecoder)
ma_src_config
Definition: miniaudio.h:935
MA_CHANNEL_AUX_16
#define MA_CHANNEL_AUX_16
Definition: miniaudio.h:685
ma_standard_channel_map_microsoft
@ ma_standard_channel_map_microsoft
Definition: miniaudio.h:822
ma_decoder::onGetLengthInPCMFrames
ma_decoder_get_length_in_pcm_frames_proc onGetLengthInPCMFrames
Definition: miniaudio.h:3209
ma_format_converter_config::noSSE2
ma_bool32 noSSE2
Definition: miniaudio.h:852
index
GLuint index
Definition: glad.h:1334
ma_rb_uninit
void ma_rb_uninit(ma_rb *pRB)
drwav_bool32
drwav_uint32 drwav_bool32
Definition: dr_wav.h:179
MA_FAILED_TO_CREATE_THREAD
#define MA_FAILED_TO_CREATE_THREAD
Definition: miniaudio.h:747
ma_context::pthread_mutex_lock
ma_proc pthread_mutex_lock
Definition: miniaudio.h:2359
MA_SAMPLE_RATE_32000
#define MA_SAMPLE_RATE_32000
Definition: miniaudio.h:756
ma_decoder::onUninit
ma_decoder_uninit_proc onUninit
Definition: miniaudio.h:3208
ma_pcm_converter
struct ma_pcm_converter ma_pcm_converter
Definition: miniaudio.h:979
ma_src_sinc_window_function_hann
@ ma_src_sinc_window_function_hann
Definition: miniaudio.h:923
ma_seek_origin_start
@ ma_seek_origin_start
Definition: miniaudio.h:3167
MA_CHANNEL_TOP_BACK_LEFT
#define MA_CHANNEL_TOP_BACK_LEFT
Definition: miniaudio.h:666
addr
GLenum const void * addr
Definition: glad.h:3632
ma_decoder::onRead
ma_decoder_read_proc onRead
Definition: miniaudio.h:3194
int
unsigned int
Definition: CMakeCache.txt:638
ma_backend_sndio
@ ma_backend_sndio
Definition: miniaudio.h:1708
ma_src_config::noAVX512
ma_bool32 noAVX512
Definition: miniaudio.h:943
ma_pcm_converter_read_proc
ma_uint32(* ma_pcm_converter_read_proc)(ma_pcm_converter *pDSP, void *pFramesOut, ma_uint32 frameCount, void *pUserData)
Definition: miniaudio.h:980
ma_pcm_converter_config_init
ma_pcm_converter_config ma_pcm_converter_config_init(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_pcm_converter_read_proc onRead, void *pUserData)
MA_CHANNEL_FRONT_CENTER
#define MA_CHANNEL_FRONT_CENTER
Definition: miniaudio.h:653
handle
EGLImageKHR EGLint EGLint * handle
Definition: eglext.h:636
ma_apply_volume_factor_pcm_frames_u8
void ma_apply_volume_factor_pcm_frames_u8(ma_uint8 *pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
ma_rb::pBuffer
void * pBuffer
Definition: miniaudio.h:1493
ma_decode_memory
ma_result ma_decode_memory(const void *pData, size_t dataSize, ma_decoder_config *pConfig, ma_uint64 *pFrameCountOut, void **ppDataOut)
ma_channel_router_config::channelsOut
ma_uint32 channelsOut
Definition: miniaudio.h:881
ma_pcm_converter_init
ma_result ma_pcm_converter_init(const ma_pcm_converter_config *pConfig, ma_pcm_converter *pDSP)
stb_vorbis_info
Definition: stb_vorbis.h:130
ma_channel
ma_uint8 ma_channel
Definition: miniaudio.h:648
ma_format_count
@ ma_format_count
Definition: miniaudio.h:808
ma_rb::subbufferSizeInBytes
ma_uint32 subbufferSizeInBytes
Definition: miniaudio.h:1494
ma_get_backend_name
const char * ma_get_backend_name(ma_backend backend)
ma_pcm_converter_config::channelsOut
ma_uint32 channelsOut
Definition: miniaudio.h:989
ma_copy_and_apply_volume_factor_u8
void ma_copy_and_apply_volume_factor_u8(ma_uint8 *pSamplesOut, const ma_uint8 *pSamplesIn, ma_uint32 sampleCount, float factor)
ma_decoder_config::src
union ma_decoder_config::@81 src
ma_context::pthread_mutex_init
ma_proc pthread_mutex_init
Definition: miniaudio.h:2357
mode
GLenum mode
Definition: glad.h:992
MA_LOG_LEVEL_WARNING
#define MA_LOG_LEVEL_WARNING
Definition: miniaudio.h:638
ma_device_info::name
char name[256]
Definition: miniaudio.h:1909
MA_SAMPLE_RATE_192000
#define MA_SAMPLE_RATE_192000
Definition: miniaudio.h:762
ma_pcm_f32_to_s24
void ma_pcm_f32_to_s24(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_pcm_converter_config::noSSE2
ma_bool32 noSSE2
Definition: miniaudio.h:997
ma_copy_and_apply_volume_factor_s24
void ma_copy_and_apply_volume_factor_s24(void *pSamplesOut, const void *pSamplesIn, ma_uint32 sampleCount, float factor)
ma_channel_router_config_init
ma_channel_router_config ma_channel_router_config_init(ma_uint32 channelsIn, const ma_channel channelMapIn[MA_MAX_CHANNELS], ma_uint32 channelsOut, const ma_channel channelMapOut[MA_MAX_CHANNELS], ma_channel_mix_mode mixingMode, ma_channel_router_read_deinterleaved_proc onRead, void *pUserData)
ma_format_converter_read_deinterleaved_proc
ma_uint32(* ma_format_converter_read_deinterleaved_proc)(ma_format_converter *pConverter, ma_uint32 frameCount, void **ppSamplesOut, void *pUserData)
Definition: miniaudio.h:842
drmp3_config::outputChannels
drmp3_uint32 outputChannels
Definition: dr_mp3.h:234
ma_device_info::minChannels
ma_uint32 minChannels
Definition: miniaudio.h:1921
ma_pcm_s24_to_s32
void ma_pcm_s24_to_s32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_device_stop
ma_result ma_device_stop(ma_device *pDevice)
name
GLuint const GLchar * name
Definition: glad.h:1334
ma_device_id::nullbackend
int nullbackend
Definition: miniaudio.h:1901
ma_format_converter::useAVX2
ma_bool32 useAVX2
Definition: miniaudio.h:865
ma_scale_buffer_size
ma_uint32 ma_scale_buffer_size(ma_uint32 baseBufferSize, float scale)
ma_pcm_converter_config::formatIn
ma_format formatIn
Definition: miniaudio.h:984
endif
glfw endif() if(NOT glfw3_FOUND AND NOT USE_EXTERNAL_GLFW STREQUAL "ON" AND "$
Definition: CMakeLists.txt:25
drflac_seek_origin_start
@ drflac_seek_origin_start
Definition: dr_flac.h:227
ma_decoder_init_memory_raw
ma_result ma_decoder_init_memory_raw(const void *pData, size_t dataSize, const ma_decoder_config *pConfigIn, const ma_decoder_config *pConfigOut, ma_decoder *pDecoder)
ma_device_config::stopCallback
ma_stop_proc stopCallback
Definition: miniaudio.h:1944
drflac_int16
int16_t drflac_int16
Definition: dr_flac.h:135
writeOffset
GLenum GLintptr GLintptr writeOffset
Definition: glad.h:1889
ma_device_config::alsa
struct ma_device_config::@70 alsa
ma_convert_frames
ma_uint64 ma_convert_frames(void *pOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void *pIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_uint64 frameCount)
ma_rb_init
ma_result ma_rb_init(size_t bufferSizeInBytes, void *pOptionalPreallocatedBuffer, ma_rb *pRB)
ma_decoder::readPointer
ma_uint64 readPointer
Definition: miniaudio.h:3197
ma_src_config_init_new
ma_src_config ma_src_config_init_new(void)
winmm
winmm
Definition: CMakeCache.txt:562
ma_thread_priority_idle
@ ma_thread_priority_idle
Definition: miniaudio.h:1723
ma_copy_and_apply_volume_factor_pcm_frames_s24
void ma_copy_and_apply_volume_factor_pcm_frames_s24(void *pPCMFramesOut, const void *pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
ma_pcm_converter_config::neverConsumeEndOfInput
ma_bool32 neverConsumeEndOfInput
Definition: miniaudio.h:996
ma_channel_router::useSSE2
ma_bool32 useSSE2
Definition: miniaudio.h:901
ma_channel_router_read_deinterleaved
ma_uint64 ma_channel_router_read_deinterleaved(ma_channel_router *pRouter, ma_uint64 frameCount, void **ppSamplesOut, void *pUserData)
ma_decoder_init_file_wav
ma_result ma_decoder_init_file_wav(const char *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_context::onDeviceMainLoop
ma_result(* onDeviceMainLoop)(ma_device *pDevice)
Definition: miniaudio.h:2026
ma_decoder_init_file_mp3
ma_result ma_decoder_init_file_mp3(const char *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_context_config::threadPriority
ma_thread_priority threadPriority
Definition: miniaudio.h:1982
ma_thread::_unused
int _unused
Definition: miniaudio.h:1751
ma_decoder_seek_proc
ma_bool32(* ma_decoder_seek_proc)(ma_decoder *pDecoder, int byteOffset, ma_seek_origin origin)
Definition: miniaudio.h:3172
ma_copy_and_apply_volume_factor_pcm_frames_f32
void ma_copy_and_apply_volume_factor_pcm_frames_f32(float *pPCMFramesOut, const float *pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
ma_device_config_init
ma_device_config ma_device_config_init(ma_device_type deviceType)
MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE
#define MA_FAILED_TO_CONFIGURE_BACKEND_DEVICE
Definition: miniaudio.h:744
drflac_close
void drflac_close(drflac *pFlac)
ma_device_config::pUserData
void * pUserData
Definition: miniaudio.h:1945
ma_context_config::pUserData
void * pUserData
Definition: miniaudio.h:1983
ma_src_config_sinc::windowFunction
ma_src_sinc_window_function windowFunction
Definition: miniaudio.h:930
ma_apply_volume_factor_pcm_frames_s32
void ma_apply_volume_factor_pcm_frames_s32(ma_int32 *pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
ma_context_config::useVerboseDeviceEnumeration
ma_bool32 useVerboseDeviceEnumeration
Definition: miniaudio.h:1987
port
uint16_t port
Definition: network_resolve_host.c:27
ma_uint8
uint8_t ma_uint8
Definition: miniaudio.h:541
ma_performance_profile
ma_performance_profile
Definition: miniaudio.h:834
ma_channel_router::isSimpleShuffle
ma_bool32 isSimpleShuffle
Definition: miniaudio.h:898
ma_device_start
ma_result ma_device_start(ma_device *pDevice)
ma_thread::pContext
ma_context * pContext
Definition: miniaudio.h:1735
ma_dither_mode_triangle
@ ma_dither_mode_triangle
Definition: miniaudio.h:793
ma_rb
Definition: miniaudio.h:1439
ma_deinterleave_pcm_frames
void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void *pInterleavedPCMFrames, void **ppDeinterleavedPCMFrames)
ma_context::backend
ma_backend backend
Definition: miniaudio.h:2006
ma_pcm_rb_get_subbuffer_stride
ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb *pRB)
MA_SIZE_MAX
#define MA_SIZE_MAX
Definition: miniaudio.h:591
ma_context::pthread_cond_init
ma_proc pthread_cond_init
Definition: miniaudio.h:2361
ma_src_config::channels
ma_uint32 channels
Definition: miniaudio.h:938
MA_OUT_OF_MEMORY
#define MA_OUT_OF_MEMORY
Definition: miniaudio.h:713
ma_standard_channel_map_sound4
@ ma_standard_channel_map_sound4
Definition: miniaudio.h:827
ma_pcm_s24_to_s16
void ma_pcm_s24_to_s16(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_decoder_config_init
ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
ma_event::posix
struct ma_event::@64::@66 posix
ma_device_info::minSampleRate
ma_uint32 minSampleRate
Definition: miniaudio.h:1923
MA_FAILED_TO_START_BACKEND_DEVICE
#define MA_FAILED_TO_START_BACKEND_DEVICE
Definition: miniaudio.h:742
MA_CHANNEL_AUX_27
#define MA_CHANNEL_AUX_27
Definition: miniaudio.h:696
ma_stream_format
ma_stream_format
Definition: miniaudio.h:779
ma_clip_samples_f32
void ma_clip_samples_f32(float *p, ma_uint32 sampleCount)
MA_CHANNEL_AUX_26
#define MA_CHANNEL_AUX_26
Definition: miniaudio.h:695
MA_ALIGNED_STRUCT
#define MA_ALIGNED_STRUCT(alignment)
Definition: miniaudio.h:628
ma_src_config_sinc
Definition: miniaudio.h:929
ma_device_config::noMMap
ma_bool32 noMMap
Definition: miniaudio.h:1970
ma_device_config::dataCallback
ma_device_callback_proc dataCallback
Definition: miniaudio.h:1943
ma_pcm_f32_to_u8
void ma_pcm_f32_to_u8(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_sine_wave::time
double time
Definition: miniaudio.h:3294
VORBIS_need_more_data
@ VORBIS_need_more_data
Definition: stb_vorbis.h:361
drmp3
Definition: dr_mp3.h:239
ma_sine_wave::periodsPerSecond
double periodsPerSecond
Definition: miniaudio.h:3292
ma_decoder::pData
const ma_uint8 * pData
Definition: miniaudio.h:3213
MA_DEVICE_TYPE_NOT_SUPPORTED
#define MA_DEVICE_TYPE_NOT_SUPPORTED
Definition: miniaudio.h:720
ma_src_config_init
ma_src_config ma_src_config_init(ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_uint32 channels, ma_src_read_deinterleaved_proc onReadDeinterleaved, void *pUserData)
ma_device_config::capture
struct ma_device_config::@68 capture
ma_context::pthread_create
ma_proc pthread_create
Definition: miniaudio.h:2355
ma_src_config::onReadDeinterleaved
ma_src_read_deinterleaved_proc onReadDeinterleaved
Definition: miniaudio.h:945
ma_src_algorithm
ma_src_algorithm
Definition: miniaudio.h:914
ma_backend_winmm
@ ma_backend_winmm
Definition: miniaudio.h:1706
ma_device_init_ex
ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config *pContextConfig, const ma_device_config *pConfig, ma_device *pDevice)
timeout
GLbitfield GLuint64 timeout
Definition: glad.h:1941
MA_MAX_CHANNELS
#define MA_MAX_CHANNELS
Definition: miniaudio.h:769
ma_channel_router_config::channelMapOut
ma_channel channelMapOut[MA_MAX_CHANNELS]
Definition: miniaudio.h:883
ma_decoder_init_mp3
ma_result ma_decoder_init_mp3(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void *pUserData, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_device_config::noDefaultQualitySRC
ma_bool32 noDefaultQualitySRC
Definition: miniaudio.h:1966
drwav::sampleRate
drwav_uint32 sampleRate
Definition: dr_wav.h:407
ma_context::pthreadSO
ma_handle pthreadSO
Definition: miniaudio.h:2354
ma_src_algorithm_sinc
@ ma_src_algorithm_sinc
Definition: miniaudio.h:916
ma_src_config_sinc::windowWidth
ma_uint32 windowWidth
Definition: miniaudio.h:931
ma_decoder::outputFormat
ma_format outputFormat
Definition: miniaudio.h:3202
offset
GLintptr offset
Definition: glad.h:1294
ma_decoder_init_file_flac
ma_result ma_decoder_init_file_flac(const char *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_decoder::onSeekToPCMFrame
ma_decoder_seek_to_pcm_frame_proc onSeekToPCMFrame
Definition: miniaudio.h:3207
DR_WAVE_FORMAT_DVI_ADPCM
#define DR_WAVE_FORMAT_DVI_ADPCM
Definition: dr_wav.h:193
ma_decoder_config::channelMixMode
ma_channel_mix_mode channelMixMode
Definition: miniaudio.h:3183
ma_pcm_converter_config
Definition: miniaudio.h:983
ma_decoder::memory
struct ma_decoder::@82 memory
ma_context_config::tryStartServer
ma_bool32 tryStartServer
Definition: miniaudio.h:1998
ma_performance_profile_conservative
@ ma_performance_profile_conservative
Definition: miniaudio.h:836
ma_channel_router::useAVX2
ma_bool32 useAVX2
Definition: miniaudio.h:902
MA_CHANNEL_AUX_7
#define MA_CHANNEL_AUX_7
Definition: miniaudio.h:676
ma_decoder_init_memory_wav
ma_result ma_decoder_init_memory_wav(const void *pData, size_t dataSize, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_rb_seek_write
ma_result ma_rb_seek_write(ma_rb *pRB, size_t offsetInBytes)
ma_rb_acquire_write
ma_result ma_rb_acquire_write(ma_rb *pRB, size_t *pSizeInBytes, void **ppBufferOut)
ma_decoder_init_file_wav_w
ma_result ma_decoder_init_file_wav_w(const wchar_t *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
MA_CHANNEL_AUX_2
#define MA_CHANNEL_AUX_2
Definition: miniaudio.h:671
ma_format_converter_config_init_deinterleaved
ma_format_converter_config ma_format_converter_config_init_deinterleaved(ma_format formatIn, ma_format formatOut, ma_uint32 channels, ma_format_converter_read_deinterleaved_proc onReadDeinterleaved, void *pUserData)
src
GLenum src
Definition: glad.h:2963
ma_int16
int16_t ma_int16
Definition: miniaudio.h:542
ma_context_config::jack
struct ma_context_config::@74 jack
ma_channel_router_config::mixingMode
ma_channel_mix_mode mixingMode
Definition: miniaudio.h:884
b
GLboolean GLboolean GLboolean b
Definition: glad.h:1621
ma_decoder_init_memory
ma_result ma_decoder_init_memory(const void *pData, size_t dataSize, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_pcm_rb::format
ma_format format
Definition: miniaudio.h:1472
ma_pcm_converter_set_sample_rate
ma_result ma_pcm_converter_set_sample_rate(ma_pcm_converter *pDSP, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
ma_decoder::onSeek
ma_decoder_seek_proc onSeek
Definition: miniaudio.h:3195
MA_CHANNEL_AUX_22
#define MA_CHANNEL_AUX_22
Definition: miniaudio.h:691
drmp3_uninit
void drmp3_uninit(drmp3 *pMP3)
int16_t
signed short int16_t
Definition: stdint.h:76
ma_context_is_loopback_supported
ma_bool32 ma_context_is_loopback_supported(ma_context *pContext)
MA_CHANNEL_BACK_LEFT
#define MA_CHANNEL_BACK_LEFT
Definition: miniaudio.h:655
ma_bool8
ma_uint8 ma_bool8
Definition: miniaudio.h:570
ma_device_config::noClip
ma_bool32 noClip
Definition: miniaudio.h:1942
ma_rb_get_subbuffer_stride
size_t ma_rb_get_subbuffer_stride(ma_rb *pRB)
ma_pcm_converter_config::ditherMode
ma_dither_mode ditherMode
Definition: miniaudio.h:993
ma_decoder_read_pcm_frames
ma_uint64 ma_decoder_read_pcm_frames(ma_decoder *pDecoder, void *pFramesOut, ma_uint64 frameCount)
DR_WAVE_FORMAT_ALAW
#define DR_WAVE_FORMAT_ALAW
Definition: dr_wav.h:191
MA_CHANNEL_TOP_FRONT_RIGHT
#define MA_CHANNEL_TOP_FRONT_RIGHT
Definition: miniaudio.h:665
ma_decoder_init_raw
ma_result ma_decoder_init_raw(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void *pUserData, const ma_decoder_config *pConfigIn, const ma_decoder_config *pConfigOut, ma_decoder *pDecoder)
ma_event::_unused
int _unused
Definition: miniaudio.h:1797
ma_get_default_buffer_size_in_frames
ma_uint32 ma_get_default_buffer_size_in_frames(ma_performance_profile performanceProfile, ma_uint32 sampleRate)
ma_thread_priority_lowest
@ ma_thread_priority_lowest
Definition: miniaudio.h:1724
ma_format_f32
@ ma_format_f32
Definition: miniaudio.h:807
y
GLint y
Definition: glad.h:1004
ma_device_config::pulse
struct ma_device_config::@71 pulse
ma_pcm_converter_config::allowDynamicSampleRate
ma_bool32 allowDynamicSampleRate
Definition: miniaudio.h:995
ma_pcm_u8_to_f32
void ma_pcm_u8_to_f32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_CHANNEL_FRONT_LEFT
#define MA_CHANNEL_FRONT_LEFT
Definition: miniaudio.h:651
ma_format_converter
Definition: miniaudio.h:862
ma_standard_channel_map
ma_standard_channel_map
Definition: miniaudio.h:821
MA_CHANNEL_AUX_31
#define MA_CHANNEL_AUX_31
Definition: miniaudio.h:700
ma_format_converter::onInterleavePCM
void(* onInterleavePCM)(void *dst, const void **src, ma_uint64 frameCount, ma_uint32 channels)
Definition: miniaudio.h:869
MA_CHANNEL_POSITION_COUNT
#define MA_CHANNEL_POSITION_COUNT
Definition: miniaudio.h:703
MA_CHANNEL_AUX_14
#define MA_CHANNEL_AUX_14
Definition: miniaudio.h:683
MA_CHANNEL_AUX_18
#define MA_CHANNEL_AUX_18
Definition: miniaudio.h:687
ma_stream_layout_deinterleaved
@ ma_stream_layout_deinterleaved
Definition: miniaudio.h:786
ma_thread_priority_realtime
@ ma_thread_priority_realtime
Definition: miniaudio.h:1729
ma_decoder_init_file_flac_w
ma_result ma_decoder_init_file_flac_w(const wchar_t *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_channel_mix_mode
ma_channel_mix_mode
Definition: miniaudio.h:812
ma_backend
ma_backend
Definition: miniaudio.h:1703
stb_vorbis_close
STBVDEF void stb_vorbis_close(stb_vorbis *f)
ma_context::pthread_mutex_destroy
ma_proc pthread_mutex_destroy
Definition: miniaudio.h:2358
values
GLenum GLsizei GLsizei GLint * values
Definition: glad.h:1950
ma_pcm_converter_set_input_sample_rate
ma_result ma_pcm_converter_set_input_sample_rate(ma_pcm_converter *pDSP, ma_uint32 sampleRateOut)
ma_convert_frames_ex
ma_uint64 ma_convert_frames_ex(void *pOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_channel channelMapOut[MA_MAX_CHANNELS], const void *pIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_channel channelMapIn[MA_MAX_CHANNELS], ma_uint64 frameCount)
ma_standard_channel_map_sndio
@ ma_standard_channel_map_sndio
Definition: miniaudio.h:828
ma_share_mode_shared
@ ma_share_mode_shared
Definition: miniaudio.h:1855
MA_SAMPLE_RATE_176400
#define MA_SAMPLE_RATE_176400
Definition: miniaudio.h:761
ma_backend_aaudio
@ ma_backend_aaudio
Definition: miniaudio.h:1714
ma_mutex::posix
struct ma_mutex::@61::@63 posix
ma_blend_f32
void ma_blend_f32(float *pOut, float *pInA, float *pInB, float factor, ma_uint32 channels)
ma_device_config::performanceProfile
ma_performance_profile performanceProfile
Definition: miniaudio.h:1940
ma_standard_channel_map_flac
@ ma_standard_channel_map_flac
Definition: miniaudio.h:825
drmp3::sampleRate
drmp3_uint32 sampleRate
Definition: dr_mp3.h:243
ma_decoder::outputChannelMap
ma_channel outputChannelMap[MA_MAX_CHANNELS]
Definition: miniaudio.h:3205
ma_pcm_rb_seek_read
ma_result ma_pcm_rb_seek_read(ma_pcm_rb *pRB, ma_uint32 offsetInFrames)
ma_mutex_unlock
void ma_mutex_unlock(ma_mutex *pMutex)
ma_standard_channel_map_webaudio
@ ma_standard_channel_map_webaudio
Definition: miniaudio.h:829
ma_copy_and_apply_volume_factor_pcm_frames_s32
void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32 *pPCMFramesOut, const ma_int32 *pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
MA_CHANNEL_LEFT
#define MA_CHANNEL_LEFT
Definition: miniaudio.h:701
ma_device_uninit
void ma_device_uninit(ma_device *pDevice)
ma_src_config::sinc
ma_src_config_sinc sinc
Definition: miniaudio.h:947
ma_format_unknown
@ ma_format_unknown
Definition: miniaudio.h:802
ma_stream_format_pcm
@ ma_stream_format_pcm
Definition: miniaudio.h:780
ma_pcm_converter_config::pUserData
void * pUserData
Definition: miniaudio.h:1002
ma_backend_pulseaudio
@ ma_backend_pulseaudio
Definition: miniaudio.h:1711
ma_decoder::dataSize
size_t dataSize
Definition: miniaudio.h:3214
ma_context_get_devices
ma_result ma_context_get_devices(ma_context *pContext, ma_device_info **ppPlaybackDeviceInfos, ma_uint32 *pPlaybackDeviceCount, ma_device_info **ppCaptureDeviceInfos, ma_uint32 *pCaptureDeviceCount)
ma_sine_wave_read_f32
ma_uint64 ma_sine_wave_read_f32(ma_sine_wave *pSineWave, ma_uint64 count, float *pSamples)
uint32_t
unsigned int uint32_t
Definition: stdint.h:80
ma_sine_wave_init
ma_result ma_sine_wave_init(double amplitude, double period, ma_uint32 sampleRate, ma_sine_wave *pSineWave)
ma_device_info::maxChannels
ma_uint32 maxChannels
Definition: miniaudio.h:1922
ma_pcm_rb_pointer_disance
ma_int32 ma_pcm_rb_pointer_disance(ma_pcm_rb *pRB)
ma_device_set_stop_callback
void ma_device_set_stop_callback(ma_device *pDevice, ma_stop_proc proc)
ma_pcm_converter_config_init_ex
ma_pcm_converter_config ma_pcm_converter_config_init_ex(ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn, ma_channel channelMapIn[MA_MAX_CHANNELS], ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, ma_channel channelMapOut[MA_MAX_CHANNELS], ma_pcm_converter_read_proc onRead, void *pUserData)
ma_stream_layout
ma_stream_layout
Definition: miniaudio.h:784
ma_device_info::id
ma_device_id id
Definition: miniaudio.h:1908
ma_clip_pcm_frames_f32
MA_INLINE void ma_clip_pcm_frames_f32(float *p, ma_uint32 frameCount, ma_uint32 channels)
Definition: miniaudio.h:3107
MA_MAX_PCM_SAMPLE_SIZE_IN_BYTES
#define MA_MAX_PCM_SAMPLE_SIZE_IN_BYTES
Definition: miniaudio.h:767
ma_context::pthread_attr_setschedpolicy
ma_proc pthread_attr_setschedpolicy
Definition: miniaudio.h:2367
ma_rb_acquire_read
ma_result ma_rb_acquire_read(ma_rb *pRB, size_t *pSizeInBytes, void **ppBufferOut)
ma_format_converter::useSSE2
ma_bool32 useSSE2
Definition: miniaudio.h:864
data
GLboolean * data
Definition: glad.h:1088
MA_CHANNEL_SIDE_RIGHT
#define MA_CHANNEL_SIDE_RIGHT
Definition: miniaudio.h:661
type
GLenum type
Definition: glad.h:160
ma_proc
void(* ma_proc)(void)
Definition: miniaudio.h:577
MA_CHANNEL_AUX_12
#define MA_CHANNEL_AUX_12
Definition: miniaudio.h:681
ma_format_converter::useAVX512
ma_bool32 useAVX512
Definition: miniaudio.h:866
ma_context::onDeviceStop
ma_result(* onDeviceStop)(ma_device *pDevice)
Definition: miniaudio.h:2025
MA_CHANNEL_AUX_4
#define MA_CHANNEL_AUX_4
Definition: miniaudio.h:673
uintptr_t
_W64 unsigned int uintptr_t
Definition: stdint.h:119
ma_mutex::pContext
ma_context * pContext
Definition: miniaudio.h:1757
stb_vorbis_get_info
STBVDEF stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f)
ma_context::onDeviceIDEqual
ma_bool32(* onDeviceIDEqual)(ma_context *pContext, const ma_device_id *pID0, const ma_device_id *pID1)
Definition: miniaudio.h:2019
ma_int64
int64_t ma_int64
Definition: miniaudio.h:546
ma_device_info::formatCount
ma_uint32 formatCount
Definition: miniaudio.h:1919
window
EGLSurface EGLNativeWindowType * window
Definition: eglext.h:697
ma_is_loopback_supported
ma_bool32 ma_is_loopback_supported(ma_backend backend)
char
signed char
Definition: CMakeCache.txt:638
readOffset
GLenum GLintptr readOffset
Definition: glad.h:1889
ma_channel_router_config::noAVX2
ma_bool32 noAVX2
Definition: miniaudio.h:887
ma_get_bytes_per_sample
ma_uint32 ma_get_bytes_per_sample(ma_format format)
state
furniture state
Definition: furmap.txt:2
drflac_read_pcm_frames_f32
drflac_uint64 drflac_read_pcm_frames_f32(drflac *pFlac, drflac_uint64 framesToRead, float *pBufferOut)
ma_backend_audio4
@ ma_backend_audio4
Definition: miniaudio.h:1709
ma_src_config::noAVX2
ma_bool32 noAVX2
Definition: miniaudio.h:942
ma_backend_webaudio
@ ma_backend_webaudio
Definition: miniaudio.h:1716
ma_pcm_converter_config::onRead
ma_pcm_converter_read_proc onRead
Definition: miniaudio.h:1001
config
EGLConfig config
Definition: eglext.h:346
ma_decoder_init_vorbis
ma_result ma_decoder_init_vorbis(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void *pUserData, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_handle
void * ma_handle
Definition: miniaudio.h:575
ma_channel_mix_mode_planar_blend
@ ma_channel_mix_mode_planar_blend
Definition: miniaudio.h:816
ma_context::pDeviceInfos
ma_device_info * pDeviceInfos
Definition: miniaudio.h:2015
ma_format_converter::config
ma_format_converter_config config
Definition: miniaudio.h:863
ma_format_s24
@ ma_format_s24
Definition: miniaudio.h:805
uint64_t
unsigned __int64 uint64_t
Definition: stdint.h:90
ma_decoder_init_file_vorbis_w
ma_result ma_decoder_init_file_vorbis_w(const wchar_t *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_get_format_name
const char * ma_get_format_name(ma_format format)
ma_context_config::pApplicationName
const char * pApplicationName
Definition: miniaudio.h:1991
ma_channel_router_config::noAVX512
ma_bool32 noAVX512
Definition: miniaudio.h:888
ma_dither_mode_rectangle
@ ma_dither_mode_rectangle
Definition: miniaudio.h:792
ma_decoder_init
ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void *pUserData, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_apply_volume_factor_f32
void ma_apply_volume_factor_f32(float *pSamples, ma_uint32 sampleCount, float factor)
MA_CHANNEL_AUX_25
#define MA_CHANNEL_AUX_25
Definition: miniaudio.h:694
ma_ptr
void * ma_ptr
Definition: miniaudio.h:576
ma_format_converter_config::pUserData
void * pUserData
Definition: miniaudio.h:858
ma_pcm_s32_to_s24
void ma_pcm_s32_to_s24(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_pcm_converter_config::formatOut
ma_format formatOut
Definition: miniaudio.h:988
ma_sine_wave::delta
double delta
Definition: miniaudio.h:3293
drflac_bool32
drflac_uint32 drflac_bool32
Definition: dr_flac.h:143
ma_decoder_config::channelMap
ma_channel channelMap[MA_MAX_CHANNELS]
Definition: miniaudio.h:3182
x
GLint GLenum GLint x
Definition: glad.h:1143
ma_device_info::maxSampleRate
ma_uint32 maxSampleRate
Definition: miniaudio.h:1924
ma_context::threadPriority
ma_thread_priority threadPriority
Definition: miniaudio.h:2008
if
c if(NOT TARGET raylib) find_package(raylib 2.0 REQUIRED) endif() target_link_libraries($
Definition: CMakeLists.txt:6
ma_bool32
ma_uint32 ma_bool32
Definition: miniaudio.h:571
ma_free
void ma_free(void *p)
ma_src_config::algorithm
ma_src_algorithm algorithm
Definition: miniaudio.h:939
ma_decoder_config::ditherMode
ma_dither_mode ditherMode
Definition: miniaudio.h:3184
drwav_seek_origin_start
@ drwav_seek_origin_start
Definition: dr_wav.h:206
ma_thread_priority_highest
@ ma_thread_priority_highest
Definition: miniaudio.h:1728
ma_sine_wave
Definition: miniaudio.h:3286
ma_rb_init_ex
ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void *pOptionalPreallocatedBuffer, ma_rb *pRB)
ma_channel_router::isPassthrough
ma_bool32 isPassthrough
Definition: miniaudio.h:897
ma_rb_available_write
ma_uint32 ma_rb_available_write(ma_rb *pRB)
ma_device_config::pStreamNamePlayback
const char * pStreamNamePlayback
Definition: miniaudio.h:1974
drmp3_seek_to_pcm_frame
drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3 *pMP3, drmp3_uint64 frameIndex)
ma_event::mutex
pthread_mutex_t mutex
Definition: miniaudio.h:1792
ma_context::onGetDeviceInfo
ma_result(* onGetDeviceInfo)(ma_context *pContext, ma_device_type deviceType, const ma_device_id *pDeviceID, ma_share_mode shareMode, ma_device_info *pDeviceInfo)
Definition: miniaudio.h:2021
ma_pcm_converter_config::channelMixMode
ma_channel_mix_mode channelMixMode
Definition: miniaudio.h:992
ma_context::pthread_attr_getschedparam
ma_proc pthread_attr_getschedparam
Definition: miniaudio.h:2368
DR_WAVE_FORMAT_MULAW
#define DR_WAVE_FORMAT_MULAW
Definition: dr_wav.h:192
ma_device_init
ma_result ma_device_init(ma_context *pContext, const ma_device_config *pConfig, ma_device *pDevice)
ma_pcm_s16_to_u8
void ma_pcm_s16_to_u8(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_DEVICE_NOT_INITIALIZED
#define MA_DEVICE_NOT_INITIALIZED
Definition: miniaudio.h:729
MA_CHANNEL_FRONT_RIGHT
#define MA_CHANNEL_FRONT_RIGHT
Definition: miniaudio.h:652
drwav_read_pcm_frames_s16
drwav_uint64 drwav_read_pcm_frames_s16(drwav *pWav, drwav_uint64 framesToRead, drwav_int16 *pBufferOut)
ma_calculate_buffer_size_in_milliseconds_from_frames
ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate)
ma_format_converter_read_proc
ma_uint32(* ma_format_converter_read_proc)(ma_format_converter *pConverter, ma_uint32 frameCount, void *pFramesOut, void *pUserData)
Definition: miniaudio.h:841
ma_stop_proc
void(* ma_stop_proc)(ma_device *pDevice)
Definition: miniaudio.h:1829
ma_format_converter_config::channels
ma_uint32 channels
Definition: miniaudio.h:848
ma_decoder::internalFormat
ma_format internalFormat
Definition: miniaudio.h:3198
ma_format_converter_init
ma_result ma_format_converter_init(const ma_format_converter_config *pConfig, ma_format_converter *pConverter)
ma_stream_layout_interleaved
@ ma_stream_layout_interleaved
Definition: miniaudio.h:785
ma_format_converter_config::ditherMode
ma_dither_mode ditherMode
Definition: miniaudio.h:851
MA_ALIGN
#define MA_ALIGN(alignment)
Definition: miniaudio.h:619
ma_malloc
void * ma_malloc(size_t sz)
ma_thread::thread
pthread_t thread
Definition: miniaudio.h:1748
ma_src_config::pUserData
void * pUserData
Definition: miniaudio.h:946
ma_context::pthread_cond_wait
ma_proc pthread_cond_wait
Definition: miniaudio.h:2363
ma_channel_map_copy
void ma_channel_map_copy(ma_channel *pOut, const ma_channel *pIn, ma_uint32 channels)
ma_decode_file
ma_result ma_decode_file(const char *pFilePath, ma_decoder_config *pConfig, ma_uint64 *pFrameCountOut, void **ppDataOut)
MA_ERROR
#define MA_ERROR
Definition: miniaudio.h:710
ma_context_config_init
ma_context_config ma_context_config_init(void)
MA_SAMPLE_RATE_11025
#define MA_SAMPLE_RATE_11025
Definition: miniaudio.h:752
ma_context_config::alsa
struct ma_context_config::@72 alsa
ma_pcm_s32_to_f32
void ma_pcm_s32_to_f32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_src_sinc_window_function_default
@ ma_src_sinc_window_function_default
Definition: miniaudio.h:925
ma_format_converter::onConvertPCM
void(* onConvertPCM)(void *dst, const void *src, ma_uint64 count, ma_dither_mode ditherMode)
Definition: miniaudio.h:868
ma_int32
int32_t ma_int32
Definition: miniaudio.h:544
ma_device_info
Definition: miniaudio.h:1906
MA_NO_DEVICE
#define MA_NO_DEVICE
Definition: miniaudio.h:723
MA_SAMPLE_RATE_96000
#define MA_SAMPLE_RATE_96000
Definition: miniaudio.h:760
ma_format_converter_config::streamFormatOut
ma_stream_format streamFormatOut
Definition: miniaudio.h:850
MA_TOO_LARGE
#define MA_TOO_LARGE
Definition: miniaudio.h:715
ma_copy_and_apply_volume_factor_f32
void ma_copy_and_apply_volume_factor_f32(float *pSamplesOut, const float *pSamplesIn, ma_uint32 sampleCount, float factor)
ma_standard_channel_map_rfc3551
@ ma_standard_channel_map_rfc3551
Definition: miniaudio.h:824
ma_device_type
ma_device_type
Definition: miniaudio.h:1846
MA_FAILED_TO_READ_DATA_FROM_DEVICE
#define MA_FAILED_TO_READ_DATA_FROM_DEVICE
Definition: miniaudio.h:738
mask
GLint GLuint mask
Definition: glad.h:1067
ma_apply_volume_factor_s16
void ma_apply_volume_factor_s16(ma_int16 *pSamples, ma_uint32 sampleCount, float factor)
MA_CHANNEL_TOP_BACK_CENTER
#define MA_CHANNEL_TOP_BACK_CENTER
Definition: miniaudio.h:667
ma_device_id
Definition: miniaudio.h:1860
MA_DEVICE_BUSY
#define MA_DEVICE_BUSY
Definition: miniaudio.h:728
ma_pcm_converter_set_output_sample_rate
ma_result ma_pcm_converter_set_output_sample_rate(ma_pcm_converter *pDSP, ma_uint32 sampleRateOut)
ma_decoder_init_memory_flac
ma_result ma_decoder_init_memory_flac(const void *pData, size_t dataSize, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_pcm_rb_init_ex
ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void *pOptionalPreallocatedBuffer, ma_pcm_rb *pRB)
ma_sine_wave::amplitude
double amplitude
Definition: miniaudio.h:3291
stb_vorbis
struct stb_vorbis stb_vorbis
Definition: stb_vorbis.h:127
ma_pcm_s24_to_u8
void ma_pcm_s24_to_u8(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_CHANNEL_AUX_20
#define MA_CHANNEL_AUX_20
Definition: miniaudio.h:689
MA_CHANNEL_AUX_8
#define MA_CHANNEL_AUX_8
Definition: miniaudio.h:677
ma_pcm_converter_config::noAVX2
ma_bool32 noAVX2
Definition: miniaudio.h:998
ma_copy_and_apply_volume_factor_s32
void ma_copy_and_apply_volume_factor_s32(ma_int32 *pSamplesOut, const ma_int32 *pSamplesIn, ma_uint32 sampleCount, float factor)
ma_standard_channel_map_default
@ ma_standard_channel_map_default
Definition: miniaudio.h:830
ma_channel_mix_mode_rectangular
@ ma_channel_mix_mode_rectangular
Definition: miniaudio.h:813
ma_pcm_rb_available_write
ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb *pRB)
ma_channel_mix_mode_default
@ ma_channel_mix_mode_default
Definition: miniaudio.h:817
ma_mutex_lock
void ma_mutex_lock(ma_mutex *pMutex)
stdint.h
MA_FAILED_TO_CREATE_MUTEX
#define MA_FAILED_TO_CREATE_MUTEX
Definition: miniaudio.h:745
ma_dither_mode_none
@ ma_dither_mode_none
Definition: miniaudio.h:791
ma_format_converter_read
ma_uint64 ma_format_converter_read(ma_format_converter *pConverter, ma_uint64 frameCount, void *pFramesOut, void *pUserData)
drwav_read_pcm_frames_s32
drwav_uint64 drwav_read_pcm_frames_s32(drwav *pWav, drwav_uint64 framesToRead, drwav_int32 *pBufferOut)
ma_decoder
Definition: miniaudio.h:3193
ma_backend_wasapi
@ ma_backend_wasapi
Definition: miniaudio.h:1704
ma_format_s16
@ ma_format_s16
Definition: miniaudio.h:804
MA_INVALID_OPERATION
#define MA_INVALID_OPERATION
Definition: miniaudio.h:712
ma_src_config::noSSE2
ma_bool32 noSSE2
Definition: miniaudio.h:941
ma_decoder_config::format
ma_format format
Definition: miniaudio.h:3179
ma_context::pthread_attr_destroy
ma_proc pthread_attr_destroy
Definition: miniaudio.h:2366
MA_SUCCESS
#define MA_SUCCESS
Definition: miniaudio.h:707
ma_decoder_init_file
ma_result ma_decoder_init_file(const char *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_channel_router::shuffleTable
ma_uint8 shuffleTable[MA_MAX_CHANNELS]
Definition: miniaudio.h:905
ma_device_set_master_gain_db
ma_result ma_device_set_master_gain_db(ma_device *pDevice, float gainDB)
ma_log_level_to_string
const char * ma_log_level_to_string(ma_uint32 logLevel)
ma_device_config::wasapi
struct ma_device_config::@69 wasapi
ma_device_info::formats
ma_format formats[ma_format_count]
Definition: miniaudio.h:1920
ma_device_type_playback
@ ma_device_type_playback
Definition: miniaudio.h:1847
ma_pcm_f32_to_s32
void ma_pcm_f32_to_s32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_device_config::bufferSizeInFrames
ma_uint32 bufferSizeInFrames
Definition: miniaudio.h:1937
ma_context::captureDeviceInfoCount
ma_uint32 captureDeviceInfoCount
Definition: miniaudio.h:2014
ma_pcm_converter_config::noNEON
ma_bool32 noNEON
Definition: miniaudio.h:1000
ma_decoder::internalChannels
ma_uint32 internalChannels
Definition: miniaudio.h:3199
ma_format
ma_format
Definition: miniaudio.h:797
MA_LOG_LEVEL_ERROR
#define MA_LOG_LEVEL_ERROR
Definition: miniaudio.h:639
ma_channel_router::useAVX512
ma_bool32 useAVX512
Definition: miniaudio.h:903
ma_context::deviceEnumLock
ma_mutex deviceEnumLock
Definition: miniaudio.h:2010
ma_rb_pointer_distance
ma_int32 ma_rb_pointer_distance(ma_rb *pRB)
MA_CHANNEL_AUX_13
#define MA_CHANNEL_AUX_13
Definition: miniaudio.h:682
ma_pcm_u8_to_s32
void ma_pcm_u8_to_s32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_src_config::noNEON
ma_bool32 noNEON
Definition: miniaudio.h:944
ma_context::onDeviceInit
ma_result(* onDeviceInit)(ma_context *pContext, const ma_device_config *pConfig, ma_device *pDevice)
Definition: miniaudio.h:2022
ma_apply_volume_factor_pcm_frames
void ma_apply_volume_factor_pcm_frames(void *pFrames, ma_uint32 frameCount, ma_format format, ma_uint32 channels, float factor)
ma_device_set_master_volume
ma_result ma_device_set_master_volume(ma_device *pDevice, float volume)
ma_src_read_deinterleaved
ma_uint64 ma_src_read_deinterleaved(ma_src *pSRC, ma_uint64 frameCount, void **ppSamplesOut, void *pUserData)
ma_event
Definition: miniaudio.h:1778
ma_apply_volume_factor_pcm_frames_s16
void ma_apply_volume_factor_pcm_frames_s16(ma_int16 *pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
ma_rb::ownsBuffer
ma_bool32 ownsBuffer
Definition: miniaudio.h:1499
ma_timer
Definition: miniaudio.h:1928
MA_INVALID_DEVICE_CONFIG
#define MA_INVALID_DEVICE_CONFIG
Definition: miniaudio.h:725
drflac_read_pcm_frames_s32
drflac_uint64 drflac_read_pcm_frames_s32(drflac *pFlac, drflac_uint64 framesToRead, drflac_int32 *pBufferOut)
ma_uint32
uint32_t ma_uint32
Definition: miniaudio.h:545
ma_context::logCallback
ma_log_proc logCallback
Definition: miniaudio.h:2007
ma_channel_router::config
ma_channel_router_config config
Definition: miniaudio.h:896
ma_rb_get_subbuffer_offset
size_t ma_rb_get_subbuffer_offset(ma_rb *pRB, size_t subbufferIndex)
buffer
GLuint buffer
Definition: glad.h:1279
ma_device_config::noPreZeroedOutputBuffer
ma_bool32 noPreZeroedOutputBuffer
Definition: miniaudio.h:1941
MA_FAILED_TO_SEND_DATA_TO_DEVICE
#define MA_FAILED_TO_SEND_DATA_TO_DEVICE
Definition: miniaudio.h:740
ma_format_s32
@ ma_format_s32
Definition: miniaudio.h:806
int64_t
signed __int64 int64_t
Definition: stdint.h:89
MA_SIMD_ALIGNMENT
#define MA_SIMD_ALIGNMENT
Definition: miniaudio.h:632
ma_channel_router_init
ma_result ma_channel_router_init(const ma_channel_router_config *pConfig, ma_channel_router *pRouter)
ma_channel_router_read_deinterleaved_proc
ma_uint32(* ma_channel_router_read_deinterleaved_proc)(ma_channel_router *pRouter, ma_uint32 frameCount, void **ppSamplesOut, void *pUserData)
Definition: miniaudio.h:876
MA_SAMPLE_RATE_384000
#define MA_SAMPLE_RATE_384000
Definition: miniaudio.h:764
drmp3_config::outputSampleRate
drmp3_uint32 outputSampleRate
Definition: dr_mp3.h:235
MA_CHANNEL_TOP_BACK_RIGHT
#define MA_CHANNEL_TOP_BACK_RIGHT
Definition: miniaudio.h:668
ma_thread::posix
struct ma_thread::@58::@60 posix
ma_pcm_converter_config::channelsIn
ma_uint32 channelsIn
Definition: miniaudio.h:985
ma_interleave_pcm_frames
void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void **ppDeinterleavedPCMFrames, void *pInterleavedPCMFrames)
drwav::channels
drwav_uint16 channels
Definition: dr_wav.h:410
ma_decoder_init_file_w
ma_result ma_decoder_init_file_w(const wchar_t *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
MA_MAX_SAMPLE_RATE
#define MA_MAX_SAMPLE_RATE
Definition: miniaudio.h:771
ma_backend_oss
@ ma_backend_oss
Definition: miniaudio.h:1710
MA_CHANNEL_AUX_11
#define MA_CHANNEL_AUX_11
Definition: miniaudio.h:680
MA_SRC_SINC_DEFAULT_WINDOW_WIDTH
#define MA_SRC_SINC_DEFAULT_WINDOW_WIDTH
Definition: miniaudio.h:774
ma_pcm_u8_to_s24
void ma_pcm_u8_to_s24(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
stb_vorbis_info::sample_rate
unsigned int sample_rate
Definition: stb_vorbis.h:131
ma_decoder::currentReadPos
size_t currentReadPos
Definition: miniaudio.h:3215
MA_NO_BACKEND
#define MA_NO_BACKEND
Definition: miniaudio.h:722
ma_zero_pcm_frames
void ma_zero_pcm_frames(void *p, ma_uint32 frameCount, ma_format format, ma_uint32 channels)
ma_apply_volume_factor_s32
void ma_apply_volume_factor_s32(ma_int32 *pSamples, ma_uint32 sampleCount, float factor)
ma_context_config::logCallback
ma_log_proc logCallback
Definition: miniaudio.h:1981
ma_context_config::pServerName
const char * pServerName
Definition: miniaudio.h:1992
ma_context::onDeviceStart
ma_result(* onDeviceStart)(ma_device *pDevice)
Definition: miniaudio.h:2024
ma_share_mode_exclusive
@ ma_share_mode_exclusive
Definition: miniaudio.h:1856
stream
EGLStreamKHR stream
Definition: eglext.h:297
ma_device_get_master_gain_db
ma_result ma_device_get_master_gain_db(ma_device *pDevice, float *pGainDB)
ma_rb_get_subbuffer_size
size_t ma_rb_get_subbuffer_size(ma_rb *pRB)
id
GLenum GLuint id
Definition: glad.h:160
MA_CHANNEL_AUX_3
#define MA_CHANNEL_AUX_3
Definition: miniaudio.h:672
MA_CHANNEL_AUX_29
#define MA_CHANNEL_AUX_29
Definition: miniaudio.h:698
MA_SAMPLE_RATE_16000
#define MA_SAMPLE_RATE_16000
Definition: miniaudio.h:753
ma_aligned_malloc
void * ma_aligned_malloc(size_t sz, size_t alignment)
ma_format_converter_config::formatOut
ma_format formatOut
Definition: miniaudio.h:847
ma_device_is_started
ma_bool32 ma_device_is_started(ma_device *pDevice)
ma_device_config::format
ma_format format
Definition: miniaudio.h:1949
ma_pcm_rb
Definition: miniaudio.h:1470
ma_context::posix
struct ma_context::@77::@80 posix
MA_SAMPLE_RATE_48000
#define MA_SAMPLE_RATE_48000
Definition: miniaudio.h:758
ma_event::condition
pthread_cond_t condition
Definition: miniaudio.h:1793
ma_timer::counterD
double counterD
Definition: miniaudio.h:1930
ma_context_config::pClientName
const char * pClientName
Definition: miniaudio.h:1997
format
GLint GLint GLsizei GLint GLenum format
Definition: glad.h:1019
ma_decoder::internalSampleRate
ma_uint32 internalSampleRate
Definition: miniaudio.h:3200
UINT32_MAX
#define UINT32_MAX
Definition: stdint.h:142
ma_pcm_s24_to_f32
void ma_pcm_s24_to_f32(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_int8
int8_t ma_int8
Definition: miniaudio.h:540
ma_format_converter_config::noAVX512
ma_bool32 noAVX512
Definition: miniaudio.h:854
ma_mutex_init
ma_result ma_mutex_init(ma_context *pContext, ma_mutex *pMutex)
ma_context::_unused
int _unused
Definition: miniaudio.h:2323
stb_vorbis_flush_pushdata
STBVDEF void stb_vorbis_flush_pushdata(stb_vorbis *f)
MA_MIN_CHANNELS
#define MA_MIN_CHANNELS
Definition: miniaudio.h:768
ma_src_set_sample_rate
ma_result ma_src_set_sample_rate(ma_src *pSRC, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
ma_device_config::periods
ma_uint32 periods
Definition: miniaudio.h:1939
drmp3_read_pcm_frames_f32
drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3 *pMP3, drmp3_uint64 framesToRead, float *pBufferOut)
drwav
Definition: dr_wav.h:385
stb_vorbis_info::channels
int channels
Definition: stb_vorbis.h:132
MA_LOG_LEVEL_INFO
#define MA_LOG_LEVEL_INFO
Definition: miniaudio.h:637
ma_uint64
uint64_t ma_uint64
Definition: miniaudio.h:547
int32_t
signed int int32_t
Definition: stdint.h:77
dst
GLenum GLenum dst
Definition: glad.h:2963
drmp3_bool32
drmp3_uint32 drmp3_bool32
Definition: dr_mp3.h:90
ma_aligned_free
void ma_aligned_free(void *p)
ma_rb_seek_read
ma_result ma_rb_seek_read(ma_rb *pRB, size_t offsetInBytes)
ma_mutex
Definition: miniaudio.h:1756
MA_CHANNEL_AUX_10
#define MA_CHANNEL_AUX_10
Definition: miniaudio.h:679
MA_INLINE
#define MA_INLINE
Definition: miniaudio.h:611
ma_channel_router::useNEON
ma_bool32 useNEON
Definition: miniaudio.h:904
ma_decoder_uninit_proc
ma_result(* ma_decoder_uninit_proc)(ma_decoder *pDecoder)
Definition: miniaudio.h:3174
ma_src_read_deinterleaved_proc
ma_uint32(* ma_src_read_deinterleaved_proc)(ma_src *pSRC, ma_uint32 frameCount, void **ppSamplesOut, void *pUserData)
Definition: miniaudio.h:911
ma_factor_to_gain_db
float ma_factor_to_gain_db(float factor)
MA_CHANNEL_AUX_19
#define MA_CHANNEL_AUX_19
Definition: miniaudio.h:688
MA_SAMPLE_RATE_352800
#define MA_SAMPLE_RATE_352800
Definition: miniaudio.h:763
ma_pcm_s32_to_u8
void ma_pcm_s32_to_u8(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_pcm_rb_get_subbuffer_offset
ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb *pRB, ma_uint32 subbufferIndex)
ma_uint16
uint16_t ma_uint16
Definition: miniaudio.h:543
params
GLenum const GLfloat * params
Definition: glad.h:1010
MA_DEVICE_UNAVAILABLE
#define MA_DEVICE_UNAVAILABLE
Definition: miniaudio.h:731
ma_decoder_uninit
ma_result ma_decoder_uninit(ma_decoder *pDecoder)
ma_decoder_init_wav
ma_result ma_decoder_init_wav(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void *pUserData, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
ma_device_config::channels
ma_uint32 channels
Definition: miniaudio.h:1950
ma_decoder::outputChannels
ma_uint32 outputChannels
Definition: miniaudio.h:3203
ma_context::pthread_cond_destroy
ma_proc pthread_cond_destroy
Definition: miniaudio.h:2362
MA_FORMAT_NOT_SUPPORTED
#define MA_FORMAT_NOT_SUPPORTED
Definition: miniaudio.h:719
ma_format_converter_config::noAVX2
ma_bool32 noAVX2
Definition: miniaudio.h:853
ma_format_u8
@ ma_format_u8
Definition: miniaudio.h:803
MA_LOG_LEVEL_VERBOSE
#define MA_LOG_LEVEL_VERBOSE
Definition: miniaudio.h:636
param
GLenum GLfloat param
Definition: glad.h:1007
ma_pcm_converter_config::sampleRateIn
ma_uint32 sampleRateIn
Definition: miniaudio.h:986
ma_decoder_config::channels
ma_uint32 channels
Definition: miniaudio.h:3180
ma_channel_router_config::noSSE2
ma_bool32 noSSE2
Definition: miniaudio.h:886
MA_CHANNEL_AUX_0
#define MA_CHANNEL_AUX_0
Definition: miniaudio.h:669
drmp3_seek_origin_start
@ drmp3_seek_origin_start
Definition: dr_mp3.h:192
ma_pcm_rb_reset
void ma_pcm_rb_reset(ma_pcm_rb *pRB)
MA_FAILED_TO_CREATE_EVENT
#define MA_FAILED_TO_CREATE_EVENT
Definition: miniaudio.h:746
z
GLdouble GLdouble GLdouble z
Definition: glad.h:1520
stb_vorbis_decode_frame_pushdata
STBVDEF int stb_vorbis_decode_frame_pushdata(stb_vorbis *f, const unsigned char *datablock, int datablock_length_in_bytes, int *channels, float ***output, int *samples)
ma_rb::encodedWriteOffset
volatile ma_uint32 encodedWriteOffset
Definition: miniaudio.h:1498
ma_pcm_rb_uninit
void ma_pcm_rb_uninit(ma_pcm_rb *pRB)
MA_TRUE
#define MA_TRUE
Definition: miniaudio.h:572
MA_CHANNEL_AUX_15
#define MA_CHANNEL_AUX_15
Definition: miniaudio.h:684
ma_device_get_master_volume
ma_result ma_device_get_master_volume(ma_device *pDevice, float *pVolume)
drwav_int16
int16_t drwav_int16
Definition: dr_wav.h:171
ma_share_mode
ma_share_mode
Definition: miniaudio.h:1854
ma_channel_router_config
Definition: miniaudio.h:879
ma_context::onEnumDevices
ma_result(* onEnumDevices)(ma_context *pContext, ma_enum_devices_callback_proc callback, void *pUserData)
Definition: miniaudio.h:2020
ma_device
struct ma_device ma_device
Definition: miniaudio.h:646
ma_decoder::internalChannelMap
ma_channel internalChannelMap[MA_MAX_CHANNELS]
Definition: miniaudio.h:3201
drflac_int32
int32_t drflac_int32
Definition: dr_flac.h:137
ma_device_config::playback
struct ma_device_config::@67 playback
ma_thread_priority_normal
@ ma_thread_priority_normal
Definition: miniaudio.h:1726
message
GLenum GLuint GLenum GLsizei const GLchar * message
Definition: glad.h:160
MA_SRC_SINC_MAX_WINDOW_WIDTH
#define MA_SRC_SINC_MAX_WINDOW_WIDTH
Definition: miniaudio.h:773
ma_apply_volume_factor_pcm_frames_s24
void ma_apply_volume_factor_pcm_frames_s24(void *pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
MA_FAILED_TO_INIT_BACKEND
#define MA_FAILED_TO_INIT_BACKEND
Definition: miniaudio.h:736
ma_context
Definition: miniaudio.h:2005
ma_channel_router_config::onReadDeinterleaved
ma_channel_router_read_deinterleaved_proc onReadDeinterleaved
Definition: miniaudio.h:890
ma_context_init
ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config *pConfig, ma_context *pContext)
ma_channel_map_blank
ma_bool32 ma_channel_map_blank(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS])
ma_enum_devices_callback_proc
ma_bool32(* ma_enum_devices_callback_proc)(ma_context *pContext, ma_device_type deviceType, const ma_device_info *pInfo, void *pUserData)
Definition: miniaudio.h:2002
ma_realloc
void * ma_realloc(void *p, size_t sz)
MA_CHANNEL_AUX_21
#define MA_CHANNEL_AUX_21
Definition: miniaudio.h:690
ma_pcm_converter_config::sinc
ma_src_config_sinc sinc
Definition: miniaudio.h:1005
ma_format_converter_config::onReadDeinterleaved
ma_format_converter_read_deinterleaved_proc onReadDeinterleaved
Definition: miniaudio.h:857
ma_pcm_rb_init
ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void *pOptionalPreallocatedBuffer, ma_pcm_rb *pRB)
ma_device_config
Definition: miniaudio.h:1934
ma_channel_router::isSimpleMonoExpansion
ma_bool32 isSimpleMonoExpansion
Definition: miniaudio.h:899
ma_channel_router_config::channelsIn
ma_uint32 channelsIn
Definition: miniaudio.h:880
ma_device_config::sampleRate
ma_uint32 sampleRate
Definition: miniaudio.h:1936
MA_DEVICE_NOT_STARTED
#define MA_DEVICE_NOT_STARTED
Definition: miniaudio.h:730
drwav_read_pcm_frames_f32
drwav_uint64 drwav_read_pcm_frames_f32(drwav *pWav, drwav_uint64 framesToRead, float *pBufferOut)
void
typedef void(APIENTRY *GLDEBUGPROC)(GLenum source
ma_mutex::mutex
pthread_mutex_t mutex
Definition: miniaudio.h:1770
result
int result
Definition: screens.h:46
dataSize
GLenum GLsizei dataSize
Definition: gl2ext.h:1256
ma_pcm_rb_get_subbuffer_ptr
void * ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb *pRB, ma_uint32 subbufferIndex, void *pBuffer)
count
GLint GLsizei count
Definition: glad.h:1134
ma_format_converter_config
Definition: miniaudio.h:845
ma_rb_reset
void ma_rb_reset(ma_rb *pRB)
drflac::sampleRate
drflac_uint32 sampleRate
Definition: dr_flac.h:497
ma_gain_db_to_factor
float ma_gain_db_to_factor(float gain)
source
GLsizei GLsizei GLchar * source
Definition: glad.h:1385
ma_context::pthread_attr_init
ma_proc pthread_attr_init
Definition: miniaudio.h:2365
MA_CHANNEL_NONE
#define MA_CHANNEL_NONE
Definition: miniaudio.h:649
ma_decoder_read_proc
size_t(* ma_decoder_read_proc)(ma_decoder *pDecoder, void *pBufferOut, size_t bytesToRead)
Definition: miniaudio.h:3171
MA_SHARE_MODE_NOT_SUPPORTED
#define MA_SHARE_MODE_NOT_SUPPORTED
Definition: miniaudio.h:721
ma_rb_available_read
ma_uint32 ma_rb_available_read(ma_rb *pRB)
ma_pcm_rb_commit_read
ma_result ma_pcm_rb_commit_read(ma_pcm_rb *pRB, ma_uint32 sizeInFrames, void *pBufferOut)
ma_channel_map_equal
ma_bool32 ma_channel_map_equal(ma_uint32 channels, const ma_channel channelMapA[MA_MAX_CHANNELS], const ma_channel channelMapB[MA_MAX_CHANNELS])
sample
Definition: jar_mod.h:108
ma_format_converter::onDeinterleavePCM
void(* onDeinterleavePCM)(void **dst, const void *src, ma_uint64 frameCount, ma_uint32 channels)
Definition: miniaudio.h:870
ma_context::pthread_join
ma_proc pthread_join
Definition: miniaudio.h:2356
ma_context_uninit
ma_result ma_context_uninit(ma_context *pContext)
ma_channel_mix_mode_custom_weights
@ ma_channel_mix_mode_custom_weights
Definition: miniaudio.h:815
ma_copy_and_apply_volume_factor_pcm_frames_u8
void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8 *pPCMFramesOut, const ma_uint8 *pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
ma_apply_volume_factor_pcm_frames_f32
void ma_apply_volume_factor_pcm_frames_f32(float *pFrames, ma_uint32 frameCount, ma_uint32 channels, float factor)
ma_format_converter_config_init
ma_format_converter_config ma_format_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channels, ma_format_converter_read_proc onRead, void *pUserData)
ma_device_config::pStreamNameCapture
const char * pStreamNameCapture
Definition: miniaudio.h:1975
ma_channel_router
Definition: miniaudio.h:895
ma_channel_mix_mode_simple
@ ma_channel_mix_mode_simple
Definition: miniaudio.h:814
ma_channel_map_contains_channel_position
ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel channelMap[MA_MAX_CHANNELS], ma_channel channelPosition)
precision
GLenum GLint GLint * precision
Definition: glad.h:2890
ma_context::onDeviceUninit
void(* onDeviceUninit)(ma_device *pDevice)
Definition: miniaudio.h:2023
drmp3_seek_origin
drmp3_seek_origin
Definition: dr_mp3.h:191
a
GLboolean GLboolean GLboolean GLboolean a
Definition: glad.h:1621
ma_decoder_config::srcAlgorithm
ma_src_algorithm srcAlgorithm
Definition: miniaudio.h:3185
ma_device_type_duplex
@ ma_device_type_duplex
Definition: miniaudio.h:1849
MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION
#define MA_SRC_SINC_LOOKUP_TABLE_RESOLUTION
Definition: miniaudio.h:775
ma_sine_wave_read_f32_ex
ma_uint64 ma_sine_wave_read_f32_ex(ma_sine_wave *pSineWave, ma_uint64 frameCount, ma_uint32 channels, ma_stream_layout layout, float **ppFrames)
ma_pcm_s16_to_s24
void ma_pcm_s16_to_s24(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
MA_CHANNEL_SIDE_LEFT
#define MA_CHANNEL_SIDE_LEFT
Definition: miniaudio.h:660
MA_CHANNEL_TOP_CENTER
#define MA_CHANNEL_TOP_CENTER
Definition: miniaudio.h:662
alpha
GLfloat GLfloat GLfloat alpha
Definition: glad.h:1031
MA_CHANNEL_AUX_17
#define MA_CHANNEL_AUX_17
Definition: miniaudio.h:686
ma_apply_volume_factor_s24
void ma_apply_volume_factor_s24(void *pSamples, ma_uint32 sampleCount, float factor)
ma_result
int ma_result
Definition: miniaudio.h:706
ma_pcm_converter_config::channelMapIn
ma_channel channelMapIn[MA_MAX_CHANNELS]
Definition: miniaudio.h:987
ma_event::pContext
ma_context * pContext
Definition: miniaudio.h:1779
ma_pcm_converter_config::noAVX512
ma_bool32 noAVX512
Definition: miniaudio.h:999
ma_context::pthread_cond_signal
ma_proc pthread_cond_signal
Definition: miniaudio.h:2364
ma_pcm_rb::channels
ma_uint32 channels
Definition: miniaudio.h:1473
ma_pcm_u8_to_s16
void ma_pcm_u8_to_s16(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
r
GLboolean r
Definition: glad.h:1621
ma_mutex_uninit
void ma_mutex_uninit(ma_mutex *pMutex)
ma_context::isBackendAsynchronous
ma_bool32 isBackendAsynchronous
Definition: miniaudio.h:2016
drwav_uninit
void drwav_uninit(drwav *pWav)
MA_CHANNEL_LFE
#define MA_CHANNEL_LFE
Definition: miniaudio.h:654
ma_src_init
ma_result ma_src_init(const ma_src_config *pConfig, ma_src *pSRC)
counter
GLuint counter
Definition: gl2ext.h:1249
ma_context::pthread_mutex_unlock
ma_proc pthread_mutex_unlock
Definition: miniaudio.h:2360
drflac_read_pcm_frames_s16
drflac_uint64 drflac_read_pcm_frames_s16(drflac *pFlac, drflac_uint64 framesToRead, drflac_int16 *pBufferOut)
ma_decoder::pInternalDecoder
void * pInternalDecoder
Definition: miniaudio.h:3210
ma_backend_jack
@ ma_backend_jack
Definition: miniaudio.h:1713
ma_context::onUninit
ma_result(* onUninit)(ma_context *pContext)
Definition: miniaudio.h:2018
MA_CHANNEL_BACK_RIGHT
#define MA_CHANNEL_BACK_RIGHT
Definition: miniaudio.h:656
ma_decoder_init_memory_vorbis
ma_result ma_decoder_init_memory_vorbis(const void *pData, size_t dataSize, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
DR_WAVE_FORMAT_ADPCM
#define DR_WAVE_FORMAT_ADPCM
Definition: dr_wav.h:189
channel
Definition: jar_mod.h:139
ma_context::null_backend
struct ma_context::@75::@79 null_backend
ma_rb_commit_read
ma_result ma_rb_commit_read(ma_rb *pRB, size_t sizeInBytes, void *pBufferOut)
MA_FAILED_TO_MAP_DEVICE_BUFFER
#define MA_FAILED_TO_MAP_DEVICE_BUFFER
Definition: miniaudio.h:734
ma_backend_null
@ ma_backend_null
Definition: miniaudio.h:1717
ma_performance_profile_low_latency
@ ma_performance_profile_low_latency
Definition: miniaudio.h:835
ma_get_default_buffer_size_in_milliseconds
ma_uint32 ma_get_default_buffer_size_in_milliseconds(ma_performance_profile performanceProfile)
ma_format_converter_config::formatIn
ma_format formatIn
Definition: miniaudio.h:846
ma_event::value
ma_uint32 value
Definition: miniaudio.h:1794
ma_context_enumerate_devices
ma_result ma_context_enumerate_devices(ma_context *pContext, ma_enum_devices_callback_proc callback, void *pUserData)
ma_device_type_capture
@ ma_device_type_capture
Definition: miniaudio.h:1848
MA_API_NOT_FOUND
#define MA_API_NOT_FOUND
Definition: miniaudio.h:724
MA_CHANNEL_FRONT_RIGHT_CENTER
#define MA_CHANNEL_FRONT_RIGHT_CENTER
Definition: miniaudio.h:658
ma_backend_coreaudio
@ ma_backend_coreaudio
Definition: miniaudio.h:1707
ma_src_config::sampleRateIn
ma_uint32 sampleRateIn
Definition: miniaudio.h:936
ma_thread_priority_low
@ ma_thread_priority_low
Definition: miniaudio.h:1725
ma_pcm_converter_config_init_new
ma_pcm_converter_config ma_pcm_converter_config_init_new(void)
len
GLenum GLsizei len
Definition: glad.h:2981
ma_backend_alsa
@ ma_backend_alsa
Definition: miniaudio.h:1712
drwav::translatedFormatTag
drwav_uint16 translatedFormatTag
Definition: dr_wav.h:416
ma_context::deviceInfoLock
ma_mutex deviceInfoLock
Definition: miniaudio.h:2011
ma_decoder_init_file_vorbis
ma_result ma_decoder_init_file_vorbis(const char *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
MA_CHANNEL_TOP_FRONT_CENTER
#define MA_CHANNEL_TOP_FRONT_CENTER
Definition: miniaudio.h:664
ma_src_algorithm_default
@ ma_src_algorithm_default
Definition: miniaudio.h:918
w
GLubyte GLubyte GLubyte GLubyte w
Definition: glad.h:1547
ma_src
struct ma_src ma_src
Definition: miniaudio.h:910
ma_decoder::pUserData
void * pUserData
Definition: miniaudio.h:3196
MA_SRC_SINC_MIN_WINDOW_WIDTH
#define MA_SRC_SINC_MIN_WINDOW_WIDTH
Definition: miniaudio.h:772
ma_context_config::tryAutoSpawn
ma_bool32 tryAutoSpawn
Definition: miniaudio.h:1993
ma_pcm_f32_to_s16
void ma_pcm_f32_to_s16(void *pOut, const void *pIn, ma_uint64 count, ma_dither_mode ditherMode)
ma_decoder_get_length_in_pcm_frames_proc
ma_uint64(* ma_decoder_get_length_in_pcm_frames_proc)(ma_decoder *pDecoder)
Definition: miniaudio.h:3175
drwav_seek_to_pcm_frame
drwav_bool32 drwav_seek_to_pcm_frame(drwav *pWav, drwav_uint64 targetFrameIndex)
ma_device_config::channelMap
ma_channel channelMap[MA_MAX_CHANNELS]
Definition: miniaudio.h:1951
ma_pcm_converter_read
ma_uint64 ma_pcm_converter_read(ma_pcm_converter *pDSP, void *pFramesOut, ma_uint64 frameCount)
ma_pcm_rb_acquire_read
ma_result ma_pcm_rb_acquire_read(ma_pcm_rb *pRB, ma_uint32 *pSizeInFrames, void **ppBufferOut)
MA_CHANNEL_MONO
#define MA_CHANNEL_MONO
Definition: miniaudio.h:650
ma_channel_router_config::pUserData
void * pUserData
Definition: miniaudio.h:891
MA_MIN_SAMPLE_RATE
#define MA_MIN_SAMPLE_RATE
Definition: miniaudio.h:770
MA_CHANNEL_AUX_23
#define MA_CHANNEL_AUX_23
Definition: miniaudio.h:692
ma_mutex::_unused
int _unused
Definition: miniaudio.h:1773
MA_INVALID_ARGS
#define MA_INVALID_ARGS
Definition: miniaudio.h:711
ma_decoder_seek_to_pcm_frame
ma_result ma_decoder_seek_to_pcm_frame(ma_decoder *pDecoder, ma_uint64 frameIndex)
ma_copy_and_apply_volume_factor_pcm_frames_s16
void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16 *pPCMFramesOut, const ma_int16 *pPCMFramesIn, ma_uint32 frameCount, ma_uint32 channels, float factor)
stb_vorbis_info::max_frame_size
int max_frame_size
Definition: stb_vorbis.h:138
ma_decoder_init_file_mp3_w
ma_result ma_decoder_init_file_mp3_w(const wchar_t *pFilePath, const ma_decoder_config *pConfig, ma_decoder *pDecoder)
INT32_MAX
#define INT32_MAX
Definition: stdint.h:137
ma_channel_router_config::noNEON
ma_bool32 noNEON
Definition: miniaudio.h:889
MA_CHANNEL_FRONT_LEFT_CENTER
#define MA_CHANNEL_FRONT_LEFT_CENTER
Definition: miniaudio.h:657
MA_CHANNEL_AUX_9
#define MA_CHANNEL_AUX_9
Definition: miniaudio.h:678
ma_format_converter::useNEON
ma_bool32 useNEON
Definition: miniaudio.h:867
MA_CHANNEL_RIGHT
#define MA_CHANNEL_RIGHT
Definition: miniaudio.h:702
ma_decoder::outputSampleRate
ma_uint32 outputSampleRate
Definition: miniaudio.h:3204
ma_context_get_device_info
ma_result ma_context_get_device_info(ma_context *pContext, ma_device_type deviceType, const ma_device_id *pDeviceID, ma_share_mode shareMode, ma_device_info *pDeviceInfo)
ma_rb_get_subbuffer_ptr
void * ma_rb_get_subbuffer_ptr(ma_rb *pRB, size_t subbufferIndex, void *pBuffer)
ma_seek_origin
ma_seek_origin
Definition: miniaudio.h:3166
ma_src_sinc_window_function_rectangular
@ ma_src_sinc_window_function_rectangular
Definition: miniaudio.h:924
MA_FAILED_TO_OPEN_BACKEND_DEVICE
#define MA_FAILED_TO_OPEN_BACKEND_DEVICE
Definition: miniaudio.h:741
ma_context_config
Definition: miniaudio.h:1980
drwav_init
drwav_bool32 drwav_init(drwav *pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void *pUserData)
ma_get_standard_channel_map
void ma_get_standard_channel_map(ma_standard_channel_map standardChannelMap, ma_uint32 channels, ma_channel channelMap[MA_MAX_CHANNELS])
drmp3_get_pcm_frame_count
drmp3_uint64 drmp3_get_pcm_frame_count(drmp3 *pMP3)
ma_format_converter_config::onRead
ma_format_converter_read_proc onRead
Definition: miniaudio.h:856